Sending Limits
How much an account can send per day, how the limits rise with verification, and what happens when a message is held or refused
Every account can send on every channel, to any destination, from its first minute. Verification is not a permission to send: it raises how much you may send per day. What stands in front of an individual message is the message itself — its content, and the pattern of sends around it.
Account levels
A level decides your daily ceilings. It never decides which channels or destinations you may reach.
| Level | How it is reached | What it changes |
|---|---|---|
| Unverified | A new account that has proven nothing yet | The lowest ceilings: 25 SMS, 5 calls and 100 conversational messages a day |
| Verified | Any one of: identity verification, a saved payment method, a settled deposit, a paid plan | The ceilings below |
| Business | Business verification (KYB) | The 10,000/day SMS ceiling, which identity verification also reaches, plus 10DLC brand and campaign registration for US and Canadian SMS |
Business verification is required for one thing:
- 10DLC. Every
smsandsms_onewaymessage to a +1 number needs an approved 10DLC campaign, and carriers register a campaign against a vetted legal entity. Sending to a +1 number without one returns403with codeten_dlc_required.
No channel asks for KYB in order to send. One-way SMS included: it sends from the first minute, within the SMS ceiling above, and every message passes Zavu's risk review. If an account is ever required to verify its business before using one-way SMS, its sends are refused with 403 kyb_required and details.dashboardUrl pointing at /kyb.
Accounts created before 24 August 2026 start at Verified.
Raising your ceilings
Do any one of these in the dashboard. Each is self-serve:
- Verify your identity under Identity verification (
/kyc): an ID document and a selfie, a few minutes. - Add a payment method under Billing: about half a minute. Nothing is charged.
- Settle a deposit under Billing.
- Subscribe to a paid plan.
Any one of them moves the account to Verified. Identity or business verification is what raises the SMS ceiling to 10,000 a day — see the note under the table.
Daily ceilings
Channels that share a budget share one counter. Counts reset at 00:00 UTC.
| Budget | Channels | Unverified | Verified |
|---|---|---|---|
| SMS | sms, sms_oneway | 25/day | 200/day, or 10,000/day once identity or business verification is approved |
| Voice | voice | 5/day | 50/day on Free, no ceiling on paid plans |
| Conversational | whatsapp, telegram, instagram, messenger | 100/day | 250/day on Free, no ceiling on paid plans or on a Free team that has added funds |
email | Plan quota | Plan quota |
- Above the first level, the SMS ceiling keys on identity, not on the level: an account that reached Verified by saving a card sends 200 a day until its identity or business verification is approved. A higher limit can be agreed for an account.
- Email has no verification gate on single sends. A sender with a verified domain sends from day one, within the plan's quota: 100 a day and 3,000 a month on Free. The monthly cap lifts once the team has added funds: past it, email keeps billing from the balance in 1,000-message blocks.
- Every plan, paid ones included, also has a rolling email ceiling by account age: 200 an hour and 1,000 per 24 hours for the first 7 days, 1,000 an hour and 10,000 per 24 hours until day 30, then 5,000 an hour and 50,000 per 24 hours. It counts the last hour and the last 24 hours rather than resetting at 00:00 UTC. Past it, the send is accepted and the message then fails with
errorCodeEMAIL_RATE_LIMIT_HOURLYorEMAIL_RATE_LIMIT_DAILY. - A daily ceiling never reduces a monthly allowance: 100 a day on the conversational group still reaches the 2,000 messages a month Free includes.
- Broadcasts count each recipient against the same ceiling. Recipients beyond it are marked
failedwitherrorCodeDAILY_LIMIT_EXCEEDEDand are not retried the next day.
Monthly allowance on the app channels
whatsapp, telegram, instagram and messenger share one monthly allowance: 2,000 messages on Free. Paid plans have no message cap. A Free team that has added funds keeps going past it, paying from the balance (pay as you go). The counter resets on the 1st at 00:00 UTC.
It counts messages in both directions. A message a contact sends you consumes one unit exactly as a message you send them does. This is the part that surprises people: a project that has sent 300 messages and received 1,700 has used the whole allowance, and the figure on the dashboard will not match a count of your own sends. The plan card breaks the total into received and sent for that reason.
What does not count:
- Messages you send from the WhatsApp Business App on your own phone while coexistence is on. Meta mirrors them to Zavu so they appear in your inbox, but Zavu did not send them and does not count them.
- Failed sends. An outbound message counts when it is accepted by the channel, not when it is created.
- SMS, voice and email. Each is billed or metered on its own terms; see the daily ceilings above and your plan's email quota.
Pay as you go
A Free team that has added funds to its balance at least once is not stopped at the allowance. Past 2,000, each started block of 1,000 messages is prepaid from the balance at $4, which is $0.004 a message, sent or received. The first block is charged on the first message past the allowance, and we email the team when it happens. The $2 monthly credit Free receives is not a deposit and does not make a team pay as you go.
When the balance cannot cover the next block, the team is at the cap below until it adds funds. Pro has no per-message charge on these channels.
What happens at the cap
On Free without funds for the next block, both directions stop:
- Sending is refused.
POST /v1/messagesanswers429 a2p_limit_exceeded, and so does every other way of sending: the dashboard composer, an inbox reply, a group reply, a reaction, an AI agent's reply, and each remaining recipient of a broadcast (markedfailedwitherrorCodeA2P_LIMIT_EXCEEDED). - Receiving is held. An incoming message on those channels is kept, but it does not appear in your inbox and fires no
message.inboundwebhook until the team pays: by adding funds or moving to a paid plan, or, for a pay-as-you-go team stopped by its spend cap, by raising the cap. Held messages are then delivered in the order they arrived, counted and charged like any other message, withproviderTimestampset to when the contact sent them andheldAtset to when they were held. An AI agent does not reply to them. We email the team the first time this happens in a month, and the inbox shows how many are waiting. An integration that only replies tomessage.inboundnever sends while messages are held, so it never sees the429: it pollsGET /v1/balance, which reports the sameheldInboundand why app messages are paused.
JSON{ "balance": 4800, "currency": "usd", "payAsYouGo": true, "spendCap": 5000, "spentThisMonth": 5000, "appMessages": { "used": 3000, "included": 2000, "paused": "spend_cap" }, "heldInbound": { "count": 7, "max": 1000, "nextExpiresAt": "2026-11-28T14:02:11.000Z" } }
appMessages.paused is allowance (Free without funds at its allowance), balance or spend_cap (pay as you go), or null while messages flow. appMessages.included is null on a paid plan.
- Held messages are kept for 30 days, up to 1,000 per team. Past either limit they are refused and not delivered later. Waiting for the month to reset does not release them.
New messages flow again on their own when the counter resets, immediately on a paid plan, or, for a team that has added funds before, as soon as the balance covers the next block.
Monthly spend cap
A team can cap what usage takes from its balance in a calendar month, from Billing in the dashboard. A Free team starts with a $50 cap when its first deposit makes it pay as you go; it can raise, lower or remove it there. Usage is everything charged per use: SMS, voice, email, AI agent replies, Functions, Memory and messages past the plan's allowance. Phone number renewals, 10DLC fees, WhatsApp accounts, connections and seats are not counted, so a cap never stops a renewal.
We email the team at 50%, 80% and 100% of the cap. Once it is reached, SMS, voice, email and AI replies answer 402 spend_cap_reached until the 1st (UTC) or until the cap is raised, and a message that fails on it carries errorCode SPEND_CAP_REACHED. App messages (WhatsApp, Telegram, Instagram, Messenger) past the allowance answer differently: 429 a2p_limit_exceeded, with details.payAsYouGo.reason set to spend_cap_reached.
The same Billing screen sets the balance below which we email you (default $5).
Risk review
Every outbound sms, sms_oneway, email and voice message is read before it is sent: the content, and how the account has been sending lately. A broadcast is read once, on its own text, rather than once per recipient.
Three things can follow.
It is sent. Nothing appears in the API, and this is what happens to ordinary traffic.
It is held. The message waits for a person. It stays queued while it waits — there is no new status for a hold, and MessageStatus is unchanged — so code that polls GET /v1/messages/{messageId} or listens for the webhooks needs no change. Approved, it sends normally and delivery continues as usual. The window is a couple of hours, not days: a one-time code that arrives late is worse than one that never arrives.
It is refused. The message moves to failed and fires message.failed. errorCode says which happened:
errorCode | What it means |
|---|---|
RISK_BLOCKED | Refused outright, without a hold |
RISK_REJECTED | Held for review, and the reviewer did not approve it |
RISK_REVIEW_EXPIRED | Held for review, and the window closed before anyone approved it |
SENDING_SUSPENDED | The account's sending was suspended while this message was queued |
An SMS that fails any of these ways is not charged: the prepaid amount is returned to your balance.
Two rules worth knowing before they surprise you:
- A call is never held. A call cannot arrive two hours late, so a call the review stops is failed rather than placed. It ends with status
failed. - A message that cannot be read is held rather than sent. When the content check is briefly unavailable, the safe answer is to wait for a person. An account with an approved business verification is unaffected, and so is one that has verified something, already sends real traffic, and has a clean recent record.
If a message was refused and you believe it should not have been, contact support with the message ID.
Errors
a2p_limit_exceeded (429)
Returned by POST /v1/messages and POST /v1/messages/{messageId}/reactions when the month's allowance on the app channels is spent and, for a team paying as you go, the balance cannot cover the next block of 1,000. Free plans only: a paid plan is never refused for volume. A pay-as-you-go refusal says so in its message, and its details.payAsYouGo carries blockSize, blockPriceCents, dashboardUrl and reason: insufficient_balance or spend_cap_reached. While incoming messages are held, details.heldInbound carries count, max and nextExpiresAt (ISO 8601, when the oldest expires).
JSON{ "error": { "code": "a2p_limit_exceeded", "message": "Your sending is paused — monthly limit reached (2,000/2,000). The allowance counts messages you receive as well as the ones you send. Upgrade to Pro for unlimited messages/mo to restore it: https://dashboard.zavu.dev/upgrade?from=a2p-limit", "details": { "limit": 2000, "used": 2000, "remaining": 0, "tier": "free", "upgrade": { "nextTier": "hobby", "nextTierName": "Pro", "nextTierPrice": "$20/mo", "upgradeUrl": "https://dashboard.zavu.dev/upgrade?from=a2p-limit" } } } }
A pay-as-you-go team stopped by its spend cap, with messages held:
JSON{ "error": { "code": "a2p_limit_exceeded", "message": "Your sending is paused: the 2,000 included messages are used and your monthly spend cap leaves no room for the next 1,000 ($4.00). Raise the cap in Billing to resume: https://dashboard.zavu.dev/billing?from=a2p-limit. The allowance counts messages you receive as well as the ones you send. ...", "details": { "limit": 2000, "used": 3000, "remaining": 0, "tier": "free", "heldInbound": { "count": 7, "max": 1000, "nextExpiresAt": "2026-11-28T14:02:11.000Z" }, "payAsYouGo": { "blockSize": 1000, "blockPriceCents": 400, "dashboardUrl": "https://dashboard.zavu.dev/billing?from=a2p-limit", "reason": "spend_cap_reached" } } } }
upgrade.nextTier is the plan's id (hobby); nextTierName is what it is sold as (Pro).
Distinct from daily_limit_exceeded, which clears at 00:00 UTC. Sending clears on the 1st of the month, when the team adds funds (pay as you go), raises its spend cap, or moves to a paid plan. Held incoming messages are different: only paying releases them (funds, a raised cap, or a paid plan), never the 1st.
daily_limit_exceeded (429)
Returned by POST /v1/messages and POST /v1/calls when the channel group's ceiling for today is spent.
JSON{ "code": "daily_limit_exceeded", "message": "Daily SMS limit reached (25/day). Verify your identity or add a payment method to raise it.", "details": { "limit": 25, "remaining": 0, "dashboardUrl": "/kyc" } }
details.limit is the ceiling that applied. Wait for 00:00 UTC, or raise it: identity verification or a payment method for the SMS, voice and conversational groups, a plan upgrade for email.
sending_suspended (403)
Returned by POST /v1/messages and POST /v1/calls when an account's sending has been suspended after the review refused several of its messages. Every channel is refused while it lasts, and a message already queued fails with errorCode SENDING_SUSPENDED.
JSON{ "code": "sending_suspended", "message": "Sending from this account is suspended after Zavu's automated risk review refused several messages. Contact support to have it reviewed.", "details": { "dashboardUrl": "/support" } }
A suspension is lifted by a person. Contact support; waiting does not clear it.
ten_dlc_required (403)
Returned by POST /v1/messages on sms and sms_oneway when the destination is a US or Canadian (+1) number and the project has no approved 10DLC campaign. Register a brand and a campaign in the dashboard; both need an approved business verification. See 10DLC.
Test-mode keys
A zv_test_ key is separate from the levels above, and narrower than they are. Whatever the account has proven:
- WhatsApp by default. Without a configured sender the key sends from Zavu's shared sandbox number, and any other channel is refused with
400 Sandbox mode only supports WhatsApp. - Only to a phone on your team. On WhatsApp, Telegram, Instagram and Messenger the recipient must be a phone number verified in your account. Anything else is refused with
403. - No calls.
POST /v1/callsrefuses a test key with400.
A number is verified from the phone itself, which is what makes the check mean something:
- In the dashboard, go to Sandbox and click Generate Verification Code.
- Scan the QR code or click Open WhatsApp on the phone you want to verify.
- Send the pre-filled
VERIFY-message to Zavu's sandbox number.
The number is marked verified automatically. Up to 5 numbers per project; a code expires after 10 minutes. There is nothing to type into the dashboard, no API call, and no support ticket.
The messages are delivered for real — a test key changes who you can reach, not whether the message arrives.