Extra numbers & On Demand
Buy an extra WhatsApp number via the public API with a tenant-scoped key. Pricing follows the On Demand model: you pay a monthly price per connected number (set per-tenant by the Pilot Status team) and get unlimited messages on every number.
The Card tab in Plans — save a card once and extra numbers bought via the API bill automatically (On Demand).
Number-scoped keys get 403 — use the tenant-scoped key (dashboard Profile → API).
GET /v1/subscription/extra-numbers — Preview (dry-run)
proratedTotal is what you are charged now; monthlyTotalBRL is what recurs every cycle after that:
You only pay for the days left in the cycle. The first charge is prorated:
proratedUnitPrice = unitPriceMonthly × (remainingDays / totalDaysInCycle). In the example above, half the cycle is left, so 2 numbers × R$14,95 = R$29,90 now, and R$59,80/month from the next cycle on.fundingSource tells you how the charge will be covered — credits, card, credits+card, or none. When it is none, canFund is false and the POST returns a checkoutUrl instead of charging.
POST /v1/subscription/extra-numbers — Buy
- Free → On Demand conversion. If you are on the Free plan, buying an extra number moves you to On Demand: your current (formerly free, 200-message-limited) number becomes a paid, unlimited number and the new number is billed too. So buying 1 extra while Free bills you for 2 numbers. Because this changes what you pay, the
POSTreturns 409CONFIRMATION_REQUIRED(with the preview) unless you sendconfirm: true. - Already On Demand / paid. Buying increments your number count; messages stay unlimited.
- Payment. The prorated cost is taken from your prepaid wallet first, then any remainder from your saved card (dashboard Plans → Cartão). With neither, the response is
{ "success": true, "charged": false, "checkoutUrl": "https://…" }— open it to pay the prorated amount; the card you use there is saved for the recurring charge. Once paid, the slot unlocks (webhook) and you can create the number withPOST /v1/numbers. - The monthly recurrence comes out of the wallet, not a separate subscription. Each cycle,
paidExtraNumbers × unit priceis debited from your credits, falling back to your saved card. Top the wallet up withPOST /v1/billing/checkout(wallet_topup), which accepts PIX — so you can run On Demand on PIX credits and never save a card.
Keep either credits or a saved card available. If a cycle’s charge cannot be funded, you get 2 days to add credits or update the card before all your numbers stop sending (inbound keeps working). Adding funds reactivates immediately.
To lower your paid capacity, see the scheduled-reduction section below.
When the payment does not go through
POST /v1/subscription/extra-numbers distinguishes three failures, and they do not share a remedy. Branch on code, never on the HTTP status: two of the three are 402.
Every one of these bodies carries
proratedTotal and currency, so you can show the amount that was attempted. INSUFFICIENT_FUNDS also carries walletReason (NO_CARD or INSUFFICIENT_CREDITS).
In all three cases the extra number was not added. Your
paidExtraNumbers is unchanged and no slot was unlocked — every write is downstream of a successful charge.The declineCode field
PAYMENT_FAILED sometimes carries declineCode. When present, it is the card network’s own decline code, verbatim (insufficient_funds, expired_card, card_not_supported, …) — safe to show to the cardholder or to branch on.
⚠️ Its absence tells you nothing. A PAYMENT_FAILED with no declineCode is a payment that did not go through for a reason we could not attribute to the card — a rate limit on the request, an expired integration key, a payment intent left in a non-succeeded state. We do not invent a code to fill the field. So:
- Treat
code: "PAYMENT_FAILED"as the decision. It is always present and always means the same thing. - Use
declineCodeonly to refine the message when it is there. - Never read “no
declineCode” as “not a decline” — and never as “the card is fine”.
declineCode never appears on INSUFFICIENT_FUNDS or PAYMENT_PROVIDER_ERROR. Neither is a card decline.Paid capacity is a 30-day entitlement
When you buy extra numbers, that capacity is yours for the full 30-day cycle. Two consequences:- Deleting a number does not reduce your paid capacity.
DELETE /v1/numbers/<id>frees the slot so you can reconnect a different number in the same cycle — your paid count and your bill stay the same until the cycle ends. - Reducing capacity is an explicit, scheduled action. You decide to lower the number of paid slots; the change takes effect at the next cycle, when billing drops to the new amount. Nothing is refunded for the running cycle.
Pay As You Go works differently
On Pay As You Go there is no capacity to buy back or schedule away — you pay for the numbers you have connected:- A number is charged when it becomes usable, prorated for the days left in the cycle. For an unofficial number that is the first connection; for an official (Meta) number it is when the Embedded Signup finishes.
- The slot you paid for lasts the cycle. Deleting a number and connecting another one in its place does not charge again.
- The next cycle bills only what is still connected. Nothing accumulates from the previous cycle, so there is nothing to schedule down — see the
409 NOT_APPLICABLE_FOR_PLANon the endpoint below. - A pairing you never finished is not a number and is not billed. The “awaiting connection” row that appears while your end user is inside the Facebook dialog is excluded from billing and from your capacity, and it is removed automatically 24h after it was created.
DELETE /v1/subscription/extra-numbers — Schedule a reduction
Schedules a reduction ofquantity paid slot(s) for the next cycle:
- Scheduled, not immediate. This cycle keeps the current capacity (no refund); the reduction applies at the next rollover, when future months bill less.
- Editable. Calling again overwrites the scheduled target. Cancel it with
DELETE …/schedule(below). - Cannot drop below connected numbers. If the target would leave your limit under the numbers you still have connected, it returns 409
NUMBER_LIMIT_EXCEEDED({ currentNumberCount, maxNumbers }) — delete a number first. - Not available on Pay As You Go. Returns 409
NOT_APPLICABLE_FOR_PLAN. There is no paid capacity to give back: Pay As You Go bills the numbers you have connected, and the next cycle already charges only what stayed connected. To pay less, delete the number — scheduling nothing is required. quantitydefaults to1and is clamped so you never go below0paid extras. Number-scoped keys get 403.
paidExtraNumbers stays at the current (this-cycle) value; scheduledPaidExtraNumbers is the target that takes effect next cycle:
DELETE /v1/subscription/extra-numbers/schedule — Cancel the scheduled reduction
Cancels a pending reduction so the next cycle keeps your current paid capacity. No-op if nothing is scheduled.POST /v1/billing/checkout — Wallet top-up / add a card
Generates a hosted checkout URL for billing actions:purpose: "wallet_topup"— top up the prepaid wallet.purpose: "add_card"— save a card for future On Demand charges.
checkoutUrl to open in the browser.