RCS Wallet & Billing
RCS messages on SendSeven are paid from a prepaid RCS wallet. The wallet belongs to your billing account, so every workspace on that billing account sends from the same balance. It is separate from your plan's included message pool and from the prepaid SMS balance.
On conversational and non-conversational agents, each RCS message also counts as a SendSeven message on your plan, like a message on any other channel: it uses your included message pool first and is then billed at your plan's message price on the monthly invoice. On newsletter agents there is no SendSeven message fee: the monthly price per recipient from the wallet is the only charge (see Newsletter agents: one price per recipient per month).
This guide explains how the wallet works and documents the wallet endpoints, the campaign cost estimate, and all RCS billing error codes.
RCS is available to accounts whose billing country is Germany (DE), or that SendSeven has granted an exception. For an account that is not eligible, the read endpoints keep working, while top-ups, the bank transfer details and turning on automatic top-up answer 403 rcs_not_available. See What eligibility gates for the full list, including agent requests and campaigns.
How the wallet works
Top-ups
You add money to the wallet with a top-up:
| Rule | Value |
|---|---|
| First top-up | At least 100 EUR net. It activates the wallet. |
| Later top-ups | At least 50 EUR net each |
| Maximum per top-up | 50,000 EUR net |
| Format | Plain decimal, at most 2 decimal places, e.g. 150 or 150.50 |
| Payment methods | card (credited once the payment succeeds) or bank_transfer (credited when the transfer arrives) |
| VAT | Amounts are net. VAT is added where it applies and shown on the invoice you receive for each top-up. |
A bank transfer credits the full amount received, including any overpayment. Unused balance is refundable (see Refunds and account closure).
Reservations and charges on delivery
Sending an RCS message happens in two steps:
- Reserve. When the message is sent, the expected charge is reserved from the wallet.
available=balance−reserved. Ifavailabledoes not cover the message, the send is refused withinsufficient_rcs_balance. - Charge or release. You are charged only when the carrier confirms the message as delivered. The final charge replaces the reservation. If the message expires undelivered, it is free and the reservation is released.
Messages from your contacts to you are always free.
Charge types
Every delivered message is charged in one of four ways. The in-app How charges work tab of the RCS wallet explains them too.
| Unit | When it applies |
|---|---|
basic | A plain text message of up to 160 characters, with no suggested replies or actions (the limit is rcs.basic_max_len in the pricing overview) |
single | Any other message: longer text, media, rich cards, carousels, or any message with suggestions. A rich card counts as one message. |
session | A conversation. When a recipient replies, the exchange becomes a session that is charged once; further messages in it are free while it lasts. The message they replied to is upgraded to a session (you pay the difference, never twice). |
mau | Monthly active user, for newsletter agents only: one charge per recipient per calendar month (Europe/Berlin), no matter how many messages they receive. It is charged when the first message of the month is delivered to that recipient; every further message to them that month costs nothing. |
Which units apply depends on your RCS agent type:
- Conversational agents use
basic,singleandsession. - Non-conversational agents always charge the message unit (
basicorsingle); there are no sessions. - Newsletter agents charge
mau. They send campaigns and platform confirmation messages only (opt-in, double opt-in and STOP confirmations are free); free-text, bot, flow and AI messages are refused. Carriers limit how many newsletters one recipient may receive per day and per month (currently 2 per day and 30 per month). Messages sent by a newsletter agent carry no SendSeven message fee and do not use your included message pool.
Your net unit prices are in your RCS offer and are returned by GET /billing/rcs-wallet/prices. Billing months follow the Europe/Berlin calendar month.
Newsletter agents: one price per recipient per month
A newsletter agent costs one flat price per recipient per calendar month: EUR 0.19 net by default, or the mau price in your RCS offer. Every newsletter that recipient receives in that month is included, up to the carrier limit of 2 newsletters per day and 30 per month. Up to 3 messages sent to the same recipient within 60 seconds count as one newsletter towards this limit. There is no price per message, and no SendSeven message fee is added on top. Opt-in, double opt-in and unsubscribe confirmations are free and do not start the monthly charge.
Welcome offer: for a limited time (the closing date is welcome_offer.cutoff in the pricing overview and is shown in the app), a billing account that submits its first RCS agent request gets EUR 0.19 net per recipient per month as a flat rate for 12 months minimum, with no additional SendSeven message fees. The pricing overview reports the offer as open, locked (secured for your account) or closed.
How this compares with per-message pricing and WhatsApp. The examples use the default prices and the SendSeven message fee of the Basic and Professional plans (EUR 0.02 per message), for newsletters sent as one message each. WhatsApp uses Meta's list price for a marketing message in Germany (EUR 0.1131, billed by Meta) plus the same SendSeven message fee. All net; your offer and plan may differ.
| Newsletters to one recipient in a month | single (EUR 0.079 + EUR 0.02 fee) | WhatsApp marketing (EUR 0.1131 + EUR 0.02 fee) | Newsletter agent (mau) |
|---|---|---|---|
| 1 | EUR 0.099 | EUR 0.133 | EUR 0.19 |
| 2 | EUR 0.198 | EUR 0.266 | EUR 0.19 |
| 4.33 (weekly) | EUR 0.43 | EUR 0.58 | EUR 0.19 |
| 8 | EUR 0.79 | EUR 1.06 | EUR 0.19 |
| 30 (daily) | EUR 2.97 | EUR 3.99 | EUR 0.19 |
- Break-even on Basic and Professional: a newsletter agent is cheaper than WhatsApp from about 1.4 newsletters per recipient per month (0.19 / 0.1331) and cheaper than
singlefrom about 1.9 (0.19 / 0.099), so in practice from the 2nd newsletter. On plans with a lower message fee the break-even againstsingleis slightly higher (about 2.0 on Scale, 2.1 on Enterprise). - 1,000 recipients, weekly: about EUR 429 (
single), EUR 576 (WhatsApp), EUR 190 (newsletter agent) per month. Daily: EUR 2,970, EUR 3,993 and EUR 190. - While the included message pool still covers the messages, no extra SendSeven fee is charged for them; without the fee a newsletter agent beats
singlefrom the 3rd newsletter and WhatsApp from the 2nd. - Per-message pricing counts messages, not newsletters. A newsletter made of 2 messages (for example an image card followed by a text) costs twice as much, so a newsletter agent pays off even sooner.
- If you send a recipient only one newsletter a month, per-message pricing is cheaper.
- The in-app calculator uses the values of
GET /rcs/agent-requests/pricing-overview; you can build the same comparison from that response.
Balance warnings
SendSeven emails the billing account's owners and admins and shows an in-app banner when:
| Warning | Condition | Can be turned off |
|---|---|---|
| Balance used up | available is 0 or below | No |
| Low balance | available is below your threshold. Default threshold: 20% of your last top-up, at least 20 EUR. You can set your own. | Yes (low_warn_enabled) |
| Forecast | At your average daily spend over the last 7 days, the balance lasts less than 3 days | Yes (forecast_warn_enabled) |
Each warning is sent at most once per day. Low-balance and forecast warnings are skipped while automatic top-up is on.
Automatic top-up
With automatic top-up, a saved card is charged for a fixed amount whenever available drops below your threshold:
- Card only. The top-up amount follows the same minimum and maximum as a manual top-up; the threshold is at least 1 EUR.
- At most 3 attempts per day. A monthly cap may apply (shown as
monthly_capon the wallet). - After 2 consecutive failed payments, automatic top-up turns itself off and the owners and admins are notified by email and in the app. Update the card and turn it on again.
When the balance runs out
- Direct sends (API, inbox, flows, bots) are refused with
insufficient_rcs_balance. With an API token,POST /messagesmay answer402right away; otherwise the message is created and then fails with the code inmeta.error_code(see Where RCS errors appear). - Campaigns are not failed. The campaign moves to status
pausedwiththrottle_reasoninsufficient_rcs_balance(orrcs_wallet_blocked), and the recipients not reached yet stay pending. After a top-up the campaign continues automatically. You can also resume it withPOST /campaigns/{campaign_id}/resume.
Wallet status
status | Meaning |
|---|---|
active | Normal operation |
blocked_negative | The balance is below zero (for example after a card payment was disputed). RCS sending is refused with rcs_wallet_blocked until a top-up brings the balance back to 0 or more. |
frozen | Sending and top-ups are on hold, for example while the account is being closed or during a payment dispute. Contact support. |
Refunds and account closure
- Unused balance is refundable on request. Contact [email protected]. Card top-ups are refunded to the card, bank transfer top-ups by bank transfer, oldest top-up first, and a credit note is issued. If the first top-up is refunded in full, the next top-up needs the 100 EUR minimum again.
- If your RCS request is cancelled, the first top-up is refunded in full.
- When you close your billing account, the wallet is frozen and settled once all open messages are final. The remaining balance is first offset against your final monthly invoice; anything left over is refunded after support confirms the amount.
- Deleting a single workspace does not refund anything: the balance belongs to the billing account.
Required scopes and access
| Operation | Requirement |
|---|---|
| Read wallet, prices, ledger, statements, usage, top-up history | billing:read, and either the owner or admin role on the billing account or the account:billing_read scope. API tokens may read. |
| Bank transfer details | Same as reading the wallet, plus an eligible account (403 rcs_not_available) |
| Top up, set up automatic top-up, change warning settings | billing:update, the owner or admin role on the billing account, and a signed-in user. API tokens, OAuth apps and embedded sessions are refused (403 rcs_interactive_user_required). Top-ups, the setup intent and turning automatic top-up on also need an eligible account (403 rcs_not_available). Top-ups are normally made in the app. |
| Campaign cost estimate | campaigns:read |
The billing account is always the one of the workspace you authenticate against; you never pass it in a request.
Endpoints
All endpoints are under https://api.sendseven.com/api/v1. Money is always a decimal string in EUR, net. Wallet and ledger amounts keep up to 6 decimal places. Errors use the shape {"detail": {"code": "...", "message": "..."}}, except request-validation errors (see HTTP status codes).
| Method | Path | Purpose |
|---|---|---|
GET | /billing/rcs-wallet | Balance, status, auto top-up, warning settings, forecast |
GET | /billing/rcs-wallet/prices | Your net RCS unit prices |
GET | /billing/rcs-wallet/ledger | Wallet transactions (paginated) |
GET | /billing/rcs-wallet/usage | Charges of a month by workspace, agent and type |
GET | /billing/rcs-wallet/statements/{month} | Monthly statement |
GET | /billing/rcs-wallet/statements/{month}.csv | Monthly statement as CSV |
GET | /billing/rcs-wallet/topups | Top-up history (paginated) |
POST | /billing/rcs-wallet/topups | Start a top-up |
GET | /billing/rcs-wallet/bank-transfer | Bank transfer details and your payment reference |
POST | /billing/rcs-wallet/auto-topup/setup-intent | Prepare saving a card for automatic top-up |
PUT | /billing/rcs-wallet/auto-topup | Turn automatic top-up on or off |
PUT | /billing/rcs-wallet/warnings | Change balance warning settings |
GET | /campaigns/{campaign_id}/rcs-estimate | What the RCS part of a campaign will reserve |
Get the wallet
curl "https://api.sendseven.com/api/v1/billing/rcs-wallet" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00"
{
"billing_account_id": "ba_123",
"exists": true,
"currency": "EUR",
"balance": "412.380000",
"reserved": "12.500000",
"available": "399.880000",
"status": "active",
"writes_enabled": true,
"enforcement_enabled": true,
"first_topup_at": "2026-09-01T09:12:44Z",
"last_topup_amount": "500.00",
"minimum_topup": "50",
"auto_topup": {
"enabled": true,
"threshold": "100.00",
"amount": "250.00",
"monthly_cap": null,
"has_payment_method": true,
"fail_count": 0,
"disabled_at": null
},
"warnings": {
"low_warn_enabled": true,
"forecast_warn_enabled": true,
"low_warn_threshold": null,
"effective_low_threshold": "100.00"
},
"forecast": {
"avg_daily_charge": "18.420000",
"days_left": "21.7"
},
"banner": null,
"role": "owner",
"can_manage": true
}
| Field | Description |
|---|---|
exists | false until the first top-up is credited. The other fields then show defaults. |
balance / reserved / available | available = balance − reserved; sends are checked against available |
status | active, blocked_negative or frozen (see Wallet status) |
writes_enabled | Whether top-ups and settings changes are possible for this billing account right now |
enforcement_enabled | Whether RCS sends are checked against the balance |
minimum_topup | 100 before the first top-up, 50 afterwards |
auto_topup | Automatic top-up settings; fail_count counts consecutive failed attempts, disabled_at is set when it turned itself off |
warnings.effective_low_threshold | The threshold actually used: your own, else 20% of the last top-up with a 20 EUR floor |
forecast.days_left | available ÷ avg_daily_charge (7-day average); null without recent usage |
banner | The in-app banner currently shown: exhausted, blocked, frozen, auto_topup_failed, low, forecast or null |
role / can_manage | Your role on the billing account; can_manage is true for owners and admins |
Get prices
curl "https://api.sendseven.com/api/v1/billing/rcs-wallet/prices" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00"
{
"currency": "EUR",
"prices_are_net": true,
"price_source": "plan",
"prices": {
"basic": "<net EUR>",
"single": "<net EUR>",
"session": "<net EUR>",
"mau": "<net EUR>"
},
"session_upgrade": {
"from_basic": "<net EUR>",
"from_single": "<net EUR>"
},
"agent_fee": {
"enabled": false,
"amount": "<net EUR>",
"grace_months": 6,
"min_monthly_revenue": "<net EUR>"
}
}
The prices are resolved exactly as a real send would be. Unit prices are decimal strings with 2 to 6 decimal places.
| Field | Description |
|---|---|
price_source | Where the prices come from, most specific first: tenant_override (this workspace), ba_override (this billing account), plan, platform_default, code_default |
prices | Net price per basic, single, session and mau unit |
session_upgrade | The extra charge when a reply turns an already charged basic or single message into a session (session price minus the unit already paid, never negative) |
agent_fee | A monthly fee for your RCS agent, if your offer includes one. When enabled is false no agent fee is charged. Otherwise it is not charged during the first grace_months after launch, nor in a month where your RCS charges reach min_monthly_revenue. |
Returns 503 rcs_prices_unavailable if the prices cannot be loaded right now; retry later.
List ledger entries
GET /billing/rcs-wallet/ledger?page=1&page_size=50
| Query parameter | Description |
|---|---|
page, page_size | Pagination (page_size 1 to 200, default 50) |
type | Comma-separated ledger types, e.g. CHARGE_SINGLE,CHARGE_SESSION |
tenant_id | Only entries of one workspace of this billing account |
month | yyyy-mm (Europe/Berlin billing month) |
Ledger types:
| Type | Meaning |
|---|---|
TOPUP / TOPUP_AUTO | Manual / automatic top-up credited |
TOPUP_REFUND | Refund of unused balance |
CHARGE_BASIC, CHARGE_SINGLE, CHARGE_SESSION, CHARGE_MAU | Charge for a delivered message |
CHARGE_SESSION_UPGRADE | A reply upgraded a charged message to a session |
AGENT_FEE | Monthly agent fee |
ADJUSTMENT | Correction made by SendSeven support |
CHARGEBACK | A disputed payment was taken back |
Charges have a negative amount. A reversed charge appears as a positive entry of the same type.
{
"items": [
{
"id": "0192f1c4-7a1e-7c3b-9d2a-5b6c7d8e9f01",
"created_at": "2026-09-21T14:03:12Z",
"type": "CHARGE_SINGLE",
"amount": "-<net EUR>",
"reserved_delta": "-<net EUR>",
"balance_after": "412.380000",
"billing_month": 202609,
"tenant_id": "tenant_abc123",
"channel_id": "ch_rcs_001",
"rbm_message_id": "b3f0c2d4-1e2f-4a5b-8c7d-9e0f1a2b3c4d",
"topup_id": null,
"unit": "single",
"unit_price": "<net EUR>",
"carrier_event_at": "2026-09-21T14:03:10Z"
}
],
"pagination": { "total": 1, "page": 1, "page_size": 50, "total_pages": 1, "has_next": false, "has_prev": false }
}
Monthly usage
GET /billing/rcs-wallet/usage?month=2026-09 returns the charges of one month (default: the current Europe/Berlin month) grouped by workspace, RCS agent (channel) and ledger type:
{
"billing_account_id": "ba_123",
"month": "2026-09",
"billing_month": 202609,
"currency": "EUR",
"total_charged": "187.620000",
"items": [
{
"tenant_id": "tenant_abc123",
"tenant_name": "Main workspace",
"channel_id": "ch_rcs_001",
"channel_name": "Shop RCS",
"type": "CHARGE_SINGLE",
"count": 1520,
"charged": "152.000000"
}
]
}
Statements and CSV export
GET /billing/rcs-wallet/statements/{month} with month as yyyy-mm:
{
"billing_account_id": "ba_123",
"month": "2026-09",
"billing_month": 202609,
"currency": "EUR",
"opening_balance": "100.000000",
"closing_balance": "412.380000",
"totals": {
"topups": "500.000000",
"charges": "187.620000",
"agent_fees": "0",
"refunds": "0",
"chargebacks": "0",
"adjustments": "0",
"net_change": "312.380000"
},
"by_type": {
"TOPUP": { "count": 1, "amount": "500.000000" },
"CHARGE_SINGLE": { "count": 1520, "amount": "-152.000000" }
},
"by_workspace": [
{ "tenant_id": "tenant_abc123", "tenant_name": "Main workspace", "charged": "187.620000", "count": 1874 }
],
"generated_at": "2026-09-27T08:00:00Z",
"is_final": false
}
In totals, money going out (charges, agent_fees, refunds, chargebacks) is a positive number; adjustments and net_change keep their sign. is_final is false while the month is still running.
GET /billing/rcs-wallet/statements/{month}.csv downloads the month's ledger as rcs-statement-yyyy-mm.csv with these columns:
created_at, billing_month, type, amount_eur, balance_after_eur, workspace_id, workspace_name, channel_id, unit, unit_price_eur, rbm_message_id, topup_id, carrier_event_at, entry_id
An invalid month returns 422 rcs_invalid_month.
Top-ups
List: GET /billing/rcs-wallet/topups?page=1&page_size=20 (page_size up to 100), newest first:
{
"items": [
{
"id": "0192f1c4-0000-7000-8000-000000000001",
"method": "bank_transfer",
"status": "credited",
"amount_net": "500.00",
"vat_amount": "95.00",
"amount_gross": "595.00",
"vat_rate": "0.19",
"requested_net": "500.00",
"received_total_gross": "595.00",
"refunded_net": null,
"reference": "RCS-4F2A9C1B7D",
"failure_code": null,
"credited_at": "2026-09-03T10:21:05Z",
"created_at": "2026-09-01T09:12:44Z"
}
],
"pagination": { "total": 1, "page": 1, "page_size": 20, "total_pages": 1, "has_next": false, "has_prev": false }
}
Top-up status values: pending, requires_action (card needs 3-D Secure), awaiting_funds (bank transfer not received yet), processing, credited, failed, canceled, refunded, partially_refunded.
Create (signed-in owner or admin; this is what the app does):
curl -X POST "https://api.sendseven.com/api/v1/billing/rcs-wallet/topups" \
-H "Authorization: Bearer <user session token>" \
-H "Content-Type: application/json" \
-d '{ "amount": "250.00", "method": "card" }'
| Field | Required | Description |
|---|---|---|
amount | Yes | Net EUR (see Top-ups) |
method | Yes | card or bank_transfer |
payment_method_id | No | A saved card to charge; the billing account's default card otherwise |
Response 201:
{
"topup_id": "0192f1c4-0000-7000-8000-000000000002",
"status": "requires_action",
"amount": "250.00",
"method": "card",
"client_secret": "<payment confirmation secret>",
"requires_action": true,
"hosted_invoice_url": null,
"bank_transfer_instructions": null,
"failure_code": null
}
- Card: check
status.creditedmeans the balance is already available.requires_actionmeans the card needs 3-D Secure confirmation, which the app completes withclient_secret. A declined card returns201withstatusfailedand afailure_code; nothing is credited. failure_codeis set only whenstatusisfailed, otherwisenull. It is always one of these short codes, never a raw message from the card issuer:card_declined,insufficient_funds,expired_card,incorrect_cvc,incorrect_number,processing_error,authentication_required,card_not_supported,currency_not_supported,try_again_later,canceled,payment_failed(anything else). Treat unknown values likepayment_failed, since the list can grow. The same codes appear infailure_codeof the top-up history.- Bank transfer: nothing is created yet. The response has
topup_idnull,statusawaiting_funds, andbank_transfer_instructionswith the same content asGET /billing/rcs-wallet/bank-transfer. Always include thereferencein the transfer. Once SendSeven has received the money, the top-up appears in the history ascreditedand its invoice is issued. - Repeating the same card top-up (same amount) within 60 seconds returns the top-up already in progress instead of charging twice.
Bank transfer details
GET /billing/rcs-wallet/bank-transfer?amount=250.00 is read-only: it creates no top-up and sends no email. amount (net EUR) is optional; with it, the response also contains the VAT and the gross amount to transfer.
| Field | Description |
|---|---|
account_holder, iban, iban_formatted, bic, bank_name | SendSeven's bank account to transfer to. The values are shown in the app; they are not published here. |
intermediary_bic | Only needed for transfers from outside the SEPA area; may be null |
reference | Your payment reference. It is fixed for your billing account (RCS- followed by 10 characters), so every bank top-up uses the same one. |
currency, vat_rate, vat_scheme | Currency (eur), the VAT rate that applies to your account and how it is applied |
min_amount_net, min_amount_gross, max_amount_net | The minimum (100 EUR net for the first top-up, then 50) and the maximum per top-up |
amount_net, vat_amount, amount_gross | For the amount you passed: net, VAT and what to transfer. null without amount. |
note_code | credit_after_receipt: the wallet is credited once the transfer has been received |
The wallet is credited with the net amount of what was received, including any overpayment. Errors: 403 rcs_not_available (account not eligible), 422 rcs_method_disabled (bank transfer not available for this account), 422 rcs_first_topup_min / rcs_min_amount for an amount below the minimum, 503 rcs_bank_details_unavailable (details not available right now).
Automatic top-up
POST /billing/rcs-wallet/auto-topup/setup-intentreturns{"client_secret": "..."}, used by the app to save a card for off-session payments.PUT /billing/rcs-wallet/auto-topup:
{ "enabled": true, "threshold": "100.00", "amount": "250.00" }
| Field | Description |
|---|---|
enabled | Turn automatic top-up on or off |
threshold | Top up when available drops below this (at least 1 EUR). Required when enabling. |
amount | Net amount per automatic top-up. Required when enabling. |
payment_method_id | Optional saved card; a card on file is required to enable |
Enabling without threshold or amount returns 422 rcs_auto_topup_incomplete. Enabling resets fail_count.
Warning settings
PUT /billing/rcs-wallet/warnings is a partial update; omit a field to leave it unchanged. Send "low_warn_threshold": null to go back to the default rule. Returns the refreshed wallet.
{ "low_warn_enabled": true, "low_warn_threshold": "150.00", "forecast_warn_enabled": false }
The wallet is created with the first top-up; before that this endpoint returns 404 rcs_wallet_not_found.
Campaign cost estimate
GET /campaigns/{campaign_id}/rcs-estimate (scope campaigns:read) shows what the RCS part of a campaign will reserve from the wallet, so you can top up before sending:
curl "https://api.sendseven.com/api/v1/campaigns/camp_123/rcs-estimate" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00"
{
"campaign_id": "camp_123",
"currency": "EUR",
"recipients": 5000,
"reachable": 4600,
"billable_recipients": 4600,
"amount": "<net EUR>",
"wallet_available": "399.880000",
"wallet_status": "active",
"shortfall": "<net EUR>",
"sufficient": false,
"billing_active": true,
"enforced": true,
"price_source": "plan",
"recipient_source": "audience",
"channels": [
{
"channel_id": "ch_rcs_001",
"agent_type": "conversational",
"recipients": 5000,
"reachable": 4200,
"unreachable": 400,
"unknown": 400,
"already_mau": 0,
"billable_recipients": 4600,
"units": ["single"],
"per_recipient": "<net EUR>",
"amount": "<net EUR>",
"mau_periods": [],
"reachability_available": true
}
]
}
| Field | Description |
|---|---|
amount | Reachable RCS recipients × the price per recipient, summed over all RCS channels of the campaign |
sufficient / shortfall | sufficient is false when the wallet does not cover amount (or the wallet is not active); shortfall is the missing amount |
wallet_available / wallet_status | The current wallet; null when no balance is reserved for this account |
billing_active / enforced | Whether the wallet is reserved from for this account and whether sends are checked against it |
recipient_source | audience (planned from the campaign's lists) or campaign_messages (a campaign already sending: only recipients not sent yet are counted) |
channels[].reachable / unreachable / unknown | Recipients last seen as reachable on RCS, not reachable (left out of the estimate), or not checked yet (counted) |
channels[].units | The units each recipient's message is expected to use, e.g. ["single"] |
channels[].already_mau | Newsletter agents: recipients already charged as a monthly active user in the month(s) in mau_periods; they are not charged again |
For newsletter agents, amount is the whole cost of the campaign: there is no SendSeven message fee. The plan cost estimate (GET /campaigns/{campaign_id}/estimate-cost) therefore leaves recipients on a newsletter agent out of total_cost_eur, breakdown and the included-pool figures, and lists them in rcs_newsletter (recipient_count, sendseven_fee_eur: 0). recipient_count still includes them.
The estimate is informational. A campaign is never refused up front: if the balance runs out while sending, it pauses and continues after a top-up (see When the balance runs out).
RCS agent requests
Before a workspace can send RCS, it needs its own RCS agent: the verified sender (name, logo, colours) that recipients see. You apply for it with an agent request. The SendSeven team submits the request to Google and the mobile carriers for verification once two things are in place:
- The request is complete and submitted.
- The billing account has a credited first RCS top-up of at least 100 EUR net (see Top-ups).
If you submit before the top-up is credited, the request waits in awaiting_topup and moves on to submitted by itself as soon as the top-up is credited. Most customers fill in the form on the RCS request page in the app, which keeps the request as a draft you can edit and save until you submit it. A draft costs nothing: money only moves with a top-up. These endpoints do the same; the app also uses the pricing overview and prefill endpoints below.
Access
| Operation | Requirement |
|---|---|
| List and get requests, pricing overview, prefill | channels:read. API tokens may read. OAuth apps and embedded sessions are refused (403 rcs_request_forbidden). Not eligibility-gated. |
| Create, edit, upload files, submit | channels:create, a signed-in user (API tokens get 403 rcs_interactive_user_required) and an eligible account: billing country DE or an exception granted by SendSeven (403 rcs_not_available). |
| Cancel | channels:create and a signed-in user; always allowed, also for accounts that are not eligible. |
Requests belong to the workspace you authenticate against; the billing account is derived from it and never passed in a request.
Endpoints
| Method | Path | Purpose |
|---|---|---|
GET | /rcs/agent-requests | List this workspace's requests, newest first (page, page_size up to 100, optional status as a comma-separated list) |
POST | /rcs/agent-requests | Create a draft (201); every field is optional while in draft |
GET | /rcs/agent-requests/pricing-overview | Current prices, SendSeven fees, welcome offer and WhatsApp reference for the request page |
GET | /rcs/agent-requests/prefill | Suggested form values from your account (never writes) |
GET | /rcs/agent-requests/{request_id} | Get one request |
PATCH | /rcs/agent-requests/{request_id} | Edit a request in draft, awaiting_topup or changes_requested. Only the fields you send change; null clears a field; nested objects (brand_contact, newsletter) are replaced as a whole. |
POST | /rcs/agent-requests/{request_id}/assets/{kind} | Upload a file as multipart field file. kind is logo, banner or registry_pdf. A new upload replaces the earlier one. |
POST | /rcs/agent-requests/{request_id}/submit | Submit for review: becomes submitted, or awaiting_topup until the first top-up is credited |
POST | /rcs/agent-requests/{request_id}/cancel | Withdraw a request in draft, awaiting_topup or changes_requested. Returns {"success": true, "id": "...", "status": "cancelled"}. Top-ups stay as wallet credit; for a refund, contact support. |
Form fields
| Field | Required to submit | Notes |
|---|---|---|
display_name | Yes | 1-40 characters |
description | Yes | 1-100 characters |
brand_color | Yes | #RRGGBB, with a contrast of at least 4.5:1 against white |
agent_type | Yes | conversational, non_conversational or newsletter |
use_case | Yes | promotional, transactional, otp or multi_use (allowed combinations below) |
contact_phone | Yes | International format, for example +4930123456 |
contact_email | Yes | Shown to recipients |
website_url, privacy_policy_url, terms_url | Yes | https:// URLs |
brand_contact | name and email | Person at the brand who confirms the registration; also job_title, phone |
brand_authorization | Yes, true | You confirm you are authorised to register the agent for this brand |
opt_in_description | Yes | How recipients agree to receive messages (up to 1,000 characters) |
triggers | Yes | What triggers the agent's messages (up to 1,000 characters) |
sample_messages | Yes | 1 to 5 example messages |
opt_out_wording | Yes | How recipients stop messages (up to 500 characters) |
expected_monthly_volume | Yes | Whole number |
newsletter | Newsletter agents: name, cadence, management_templates | cadence: daily, weekly, biweekly, monthly or irregular; management_templates: up to 10 texts such as the subscribe and unsubscribe confirmations |
Files:
kind | Format | Limit |
|---|---|---|
logo | PNG or JPEG, 224 x 224 px | 50 KB |
banner | PNG or JPEG, 1440 x 448 px | 200 KB |
registry_pdf | PDF extract from the commercial register | 10 MB |
Allowed agent_type / use_case combinations:
agent_type | Allowed use_case |
|---|---|
conversational | promotional, transactional, multi_use |
non_conversational | promotional, transactional, otp, multi_use |
newsletter | promotional |
Unknown fields are rejected.
Response
{
"id": "0192f1c4-0000-7000-8000-0000000000a1",
"status": "awaiting_topup",
"agent_type": "conversational",
"use_case": "transactional",
"billing_category": "CONVERSATIONAL",
"form": { "display_name": "Acme Support", "brand_color": "#1A73E8" },
"assets": {
"logo": { "content_type": "image/png", "size": 18342, "width": 224, "height": 224, "filename": "logo.png", "uploaded_at": "2026-09-20T08:14:02Z", "url": "https://..." }
},
"carrier_states": { "telekom": { "state": "not_submitted" } },
"missing_fields": [],
"topup_required": true,
"first_topup_minimum_net": "100.00",
"review_note": null,
"channel_id": null,
"google_agent_id": null,
"submitted_at": null,
"launched_at": null,
"cancelled_at": null,
"created_at": "2026-09-20T08:10:11Z",
"updated_at": "2026-09-20T08:20:45Z"
}
missing_fieldslists what is still needed before you can submit (form paths such asbrand_contact.email, andassets.logo); it is empty outside the editable statuses.topup_requiredistruewhile the first top-up of at leastfirst_topup_minimum_netis outstanding.review_notecarries the review team's note when changes are requested.urlof the registry PDF is alwaysnull.billing_categoryis what the agent is registered and priced as: newsletter agents areNON_CONVERSATIONAL.carrier_states[...].state:not_submitted,submitted,in_review,launched,rejected.- Once the agent is live,
channel_idis the RCS channel you send from.
Request status values:
| Status | Meaning |
|---|---|
draft | Being filled in |
awaiting_topup | Submitted, waiting for the first top-up of at least 100 EUR net; moves on automatically |
submitted | With the SendSeven team |
in_review | Under review by Google and the carriers |
changes_requested | Update the request as described in review_note, then submit again |
brand_verified | The brand is verified; carrier approval is pending |
launched_partial | Live on some carriers |
launched | Live |
cancelled | Withdrawn |
offboarded | No longer active |
Pricing overview
GET /rcs/agent-requests/pricing-overview returns everything the request page shows about prices. It works before the wallet exists and for accounts that are not eligible (eligible tells you which). All money values are net EUR decimal strings with 2 to 6 decimal places.
{
"currency": "EUR",
"prices_are_net": true,
"eligible": true,
"rcs": {
"basic": "0.065",
"single": "0.079",
"session": "0.11",
"mau": "0.19",
"session_upgrade": { "from_basic": "0.045", "from_single": "0.031" },
"agent_fee": { "enabled": false, "amount": "100.00", "grace_months": 6, "min_monthly_revenue": "50.00" },
"basic_max_len": 160
},
"welcome_offer": { "state": "open", "mau_price": "0.19", "cutoff": "<ISO 8601 UTC>", "guarantee_months": 12 },
"agent_types": [
{ "type": "newsletter", "billing": "mau", "unit": "mau", "unit_price": "0.19", "sendseven_fee_per_message": "0.00" },
{ "type": "non_conversational", "billing": "per_message", "unit": "single", "unit_price": "0.079", "sendseven_fee_per_message": "0.02" },
{ "type": "conversational", "billing": "per_message", "unit": "session", "unit_price": "0.11", "sendseven_fee_per_message": "0.02" }
],
"sendseven_fee": {
"campaign_message": "0.02",
"message": "0.02",
"included_messages": 2500,
"included_per_billable_channel": false,
"applies_to_rcs_per_message": true
},
"whatsapp_reference": {
"marketing_message_de": "0.1131",
"source": "meta_list_price",
"sendseven_fee_per_message": "0.02",
"as_of": "2026-09"
}
}
The example shows the default list prices on the Professional plan; your response has your own prices and plan fees.
| Field | Description |
|---|---|
eligible | Whether this account may request agents, top up and send RCS campaigns |
rcs | Your unit prices, resolved as for a real send (same values as GET /billing/rcs-wallet/prices), plus basic_max_len, the text length up to which a plain message is basic |
welcome_offer.state | open (you can still get the offer), locked (secured for your billing account) or closed. mau_price and guarantee_months are null when closed. cutoff is the closing date, or null if none is set. |
agent_types[] | Per agent type: how it is billed, the unit it is priced by, that unit's price, and the SendSeven message fee added per message. Non-conversational sends use the campaign message fee; conversational sends use the conversation message fee. Non-conversational agents also send basic messages; conversational agents start with basic/single and upgrade to session on a reply (see Charge types). |
sendseven_fee | Your plan's SendSeven fees per campaign message and per conversation message, and the included message pool. included_per_billable_channel is true on plans whose pool grows with each billable channel. applies_to_rcs_per_message is true: per-message RCS sends carry the fee and use the pool. Newsletter agents never do. |
whatsapp_reference | For comparisons: Meta's list price for a WhatsApp marketing message in Germany (billed by Meta, not by SendSeven), your SendSeven fee per campaign message, and the month the Meta price was taken from. null when no reference price is available; hide the WhatsApp comparison then. |
Returns 503 rcs_prices_unavailable if the prices cannot be loaded right now.
Prefill suggestions
GET /rcs/agent-requests/prefill returns what your account already knows, as suggestions for an empty request form. It never writes anything. Only use a suggestion for a field the user has not filled in, and let them confirm it.
{
"suggestions": {
"display_name": "Acme",
"website_url": "https://acme.example",
"contact_email": "[email protected]",
"contact_phone": "+4930123456",
"brand_contact": { "name": "Jane Doe", "email": "[email protected]" }
},
"context": {
"company_legal_name": "Acme GmbH",
"billing_country": "DE",
"billing_city": "Berlin",
"default_language": "de",
"logo_url": "https://..."
}
}
| Field | Source |
|---|---|
suggestions.display_name | Your company name, else the workspace name. A name longer than 40 characters is skipped, never cut. |
suggestions.website_url | The workspace website, else the billing account website; https:// only (a bare domain gets https://, an http:// address is left out) |
suggestions.contact_email | The signed-in user's email |
suggestions.contact_phone | The workspace company phone, if it is in international format |
suggestions.brand_contact | The signed-in user's name and email |
context.company_legal_name | The company's legal name, for reference |
context.billing_country / billing_city | From the billing address (city falls back to the company city) |
context.default_language | The workspace's default language |
context.logo_url | A temporary link to the workspace logo, if there is one. Upload the agent logo separately as assets/logo (224 x 224 px). |
Every value can be null, and brand_contact is null when neither name nor email is known. Every non-null suggestion passes the form validation as-is. Returns 503 rcs_prefill_unavailable if the account data cannot be loaded right now.
Agent request errors
| HTTP | Code | Meaning |
|---|---|---|
| 403 | rcs_request_forbidden | This credential cannot access agent requests |
| 403 | rcs_interactive_user_required | Changes need a signed-in user, not an API token |
| 403 | rcs_not_available | RCS is not available for this account (billing country is not DE and no exception was granted) |
| 404 | rcs_billing_account_not_found | The workspace has no billing account |
| 404 | rcs_request_not_found | No such request in this workspace |
| 404 | rcs_asset_kind_invalid | kind must be logo, banner or registry_pdf |
| 409 | rcs_agent_requests_disabled / rcs_billing_writes_disabled | Agent requests are not available for this account yet |
| 409 | rcs_request_not_editable | The request's status does not allow changes |
| 409 | rcs_invalid_transition | The request cannot be submitted or cancelled in its current status. Once it is with the review team, contact support to cancel it. |
| 413 | rcs_asset_too_large | The file is above the limit for its kind |
| 415 | rcs_asset_invalid_type | Wrong file type, or the content does not match the file type |
| 422 | rcs_request_incomplete | Required fields or files are missing; fields in detail lists them |
| 422 | rcs_d23_use_case_not_allowed | use_case is not allowed for this agent_type; detail names both |
| 422 | rcs_brand_color_invalid / rcs_brand_color_contrast | Not a #RRGGBB colour, or not enough contrast against white |
| 422 | rcs_asset_empty / rcs_asset_wrong_dimensions / rcs_asset_corrupt | The file is empty, has the wrong size in pixels, or cannot be read |
| 422 | rcs_status_invalid | Unknown value in the status filter |
| 503 | rcs_storage_unavailable | The file could not be stored; try again |
| 503 | rcs_prices_unavailable / rcs_prefill_unavailable | The pricing overview or prefill could not be loaded; try again |
Error codes
Where RCS errors appear
- Synchronous: wallet endpoints and
POST /messagesreturn an HTTP error with{"detail": {"code": "...", "message": "..."}}. - On the message: when a send is refused or fails after the message was created, the message moves to
failedandmeta.error_codeholds the code (also in themessage.failedwebhook). - On a campaign: a campaign paused for the RCS balance has
statuspausedandthrottle_reasonset to the code.
Send refusals
| Code | Where | Meaning | What to do |
|---|---|---|---|
insufficient_rcs_balance | 402 on POST /messages (API tokens), meta.error_code, campaign throttle_reason | available does not cover the message | Top up; paused campaigns continue automatically |
rcs_wallet_blocked | 402 on POST /messages (API tokens), meta.error_code, campaign throttle_reason | The wallet is not active (balance below zero, or frozen) | Top up to bring the balance to 0 or more; contact support if frozen |
rcs_newsletter_free_text_off | 409 on POST /messages, meta.error_code in flows | Newsletter agents only send campaigns and confirmation messages | Send through a campaign, or use a conversational agent |
rcs_newsletter_limit_reached | meta.error_code (campaign recipient fails) | The recipient already received the carrier's maximum number of newsletters for the day or month | Not retried for this recipient; send later |
rcs_newsletter_stop_confirm_off | meta.error_code | A STOP confirmation was requested on a newsletter agent, where carriers do not allow it | None; the opt-out itself is still honoured |
rcs_not_reachable | meta.error_code | The recipient's device or network does not support RCS for your agent | Use another channel for this contact. There is no automatic fallback to SMS. |
rcs_ttl_expired | meta.error_code | The message was not delivered before it expired and was withdrawn. Not charged. | Send again if still relevant |
rcs_ttl_revoke_failed | meta.error_code | The message expired but could not be withdrawn. If it is delivered later anyway, it is charged and its status becomes delivered. | None |
rcs_billing_unavailable | meta.error_code | The balance could not be checked for a moment | Retried automatically |
rcs_billing_writes_disabled | meta.error_code | RCS billing is not enabled for this billing account yet | Contact support |
rcs_missing_deterministic_id | meta.error_code | Internal consistency check refused the send | Contact support with the message id |
rcs_send_already_started | meta.error_code | A duplicate attempt of a send that is already in progress was stopped, so the recipient is not charged twice | None |
rcs_lease_exhausted | campaigns (not shown on messages) | A campaign's pre-reserved balance was used up; the send is retried once directly against the wallet | None |
Campaign pause reasons
throttle_reason | Meaning | Resumes |
|---|---|---|
insufficient_rcs_balance | The wallet cannot fund the next RCS recipients | Automatically after a top-up, or with POST /campaigns/{campaign_id}/resume |
rcs_wallet_blocked | The wallet is not active | Automatically once the wallet is active and covers the next send again |
rcs_not_available | A campaign scheduled on RCS reached its send time while the account is not eligible for RCS. Nothing was sent. | Never automatically. Remove RCS from the campaign's targeting (PUT /campaigns/{campaign_id}), then POST /campaigns/{campaign_id}/resume |
The recipients not reached yet stay pending and are not marked failed. A campaign resumes automatically only when the wallet is active and covers the next batch of sends.
Wallet API errors
| HTTP | Code | Meaning |
|---|---|---|
| 403 | rcs_billing_forbidden | This credential or user may not access the wallet (needs owner/admin or account:billing_read; writes need owner/admin) |
| 403 | rcs_interactive_user_required | Top-ups and settings changes need a signed-in user, not an API token |
| 403 | rcs_not_available | RCS is not available for this account: top-ups, bank transfer details and turning on automatic top-up are refused |
| 404 | rcs_billing_account_not_found | The workspace has no billing account |
| 404 | rcs_workspace_not_found | The tenant_id filter is not a workspace of this billing account |
| 404 | rcs_wallet_not_found | The wallet is created with the first top-up |
| 409 | rcs_billing_writes_disabled / rcs_writes_disabled | Top-ups or settings changes are not available for this account right now |
| 409 | rcs_no_billing_customer | The billing account has no payment profile yet; add a payment method in billing settings first |
| 409 | rcs_wallet_blocked | The wallet is frozen or the billing account is scheduled for deletion; no top-ups or automatic top-up changes |
| 422 | rcs_first_topup_min | The first top-up must be at least 100 EUR net |
| 422 | rcs_min_amount | A top-up must be at least 50 EUR net |
| 422 | rcs_method_disabled | Bank transfer top-ups are not available for this account |
| 422 | rcs_no_payment_method | No usable card on file (card top-up or automatic top-up) |
| 422 | rcs_invalid_amount | Amount not a positive EUR amount in whole cents or above 50,000; for automatic top-up also a missing amount, a threshold below 1 EUR, or an amount above the monthly cap |
| 422 | rcs_invalid_method | method must be card or bank_transfer |
| 422 | rcs_invalid_month | month must be yyyy-mm |
| 422 | rcs_invalid_ledger_type | Unknown ledger type filter |
| 422 | rcs_auto_topup_incomplete | threshold and amount are required to enable automatic top-up |
| 422 | rcs_invalid_warning_settings | low_warn_enabled / forecast_warn_enabled must be true or false |
| 500 | rcs_topup_failed, rcs_setup_intent_failed, rcs_auto_topup_failed, rcs_bank_details_failed | Unexpected error; nothing was charged twice, retry later |
| 502 | rcs_stripe_error | The payment provider could not start the payment |
| 503 | rcs_prices_unavailable | Prices cannot be loaded right now |
| 503 | rcs_topups_unavailable | Top-ups are temporarily unavailable |
| 503 | rcs_bank_details_unavailable | The bank transfer details are not available right now |
HTTP status codes
| Status | Meaning for RCS |
|---|---|
402 Payment Required | The RCS balance does not cover the message (insufficient_rcs_balance) or the wallet is not active (rcs_wallet_blocked). Top up and retry. |
409 Conflict | The request conflicts with the current state: a newsletter agent refusing free text, a frozen wallet, or billing changes not available for the account. Retrying without a change does not help. |
422 Unprocessable Entity | The request is invalid. Codes starting with rcs_ use the {"detail": {"code", "message"}} shape; malformed bodies (for example an amount with 3 decimals or an unknown field) return the standard validation shape {"detail": [{"loc": [...], "msg": "...", "type": "..."}]}. |
503 Service Unavailable | Temporary; retry later with backoff |
Next Steps
- RCS Business Messaging -- eligibility, capabilities and sending
- Messaging Campaigns -- pause, resume and
throttle_reason - Webhook Events --
message.failedwithmeta.error_code