Guides
Credit
You read the balance, movement history and tax documents through both API and MCP. Paying by card - one-off or automatic - always goes through a Stripe Checkout link that a human completes; the API itself never charges a card on its own. The same goes for the optional monthly plan.
Money without VAT, hellers, the language of descriptions
Every amount in this API is an integer in HELLERS and exclusive of VAT - volai is VAT-registered, but VAT is only computed when you top up credit by card (see /en/pricing). Movement descriptions (GET /v1/credit/ledger) are composed by the API in English from the movement type - the stored Czech note never goes out, in any field.
Balance, runway and notices
GET /v1/balance returns the current balance plus two additional computations ADDITIVELY (existing clients that only read balanceHal/balanceCzk/currency are not broken): runway (dailyAverageHal - the recent average daily spend, runwayDays - how many days the balance lasts at this pace, null when it can't be computed) and notice - the same notice you see on the portal's overview page, null when there is none. Eight possible notice shapes: {kind:"unknown"} (not enough data to estimate), {kind:"empty"} (zero balance), {kind:"number_fee",feeHal,dueAt} (a monthly fee for voice numbers or SMS numbers falls due within 3 days and the balance does not cover it; feeHal is the sum of all such fees, dueAt the earliest due time; not returned while auto top-up is on), {kind:"runway",days} (the balance runs out in days days at the current pace) and four shapes only on an account with a monthly plan: {kind:"subscription_exhausted_topup",renewsAt} (this period's monthly credit is used up and the plan runs in top-up mode, so further use is paid from topped-up credit; renewsAt is the end of the period in ms, when the next monthly credit arrives), {kind:"subscription_cap_reached",renewsAt} (the monthly credit is used up, the plan runs with the cap and the topped-up credit covers nothing, so paid actions stop until renewsAt or until you top up by hand) and {kind:"subscription_past_due"} (the plan's renewal failed, the saved card could not be charged; pay the renewal in the portal or replace the card) and {kind:"subscription_runway",renewsAt,runsOutAt} (the balance runs out before the next monthly credit arrives at renewsAt; runsOutAt is the estimate in ms, from the plan's cautious pace, the same as the monthly credit outlook on the plan card in the portal: the highest of the 28-day average (only with a complete credit history), the average usage over the last 7 Prague days without number fees and the average since the period started (after its first week); runway keeps the 28-day average; a subscriber gets this shape instead of runway, and nothing while auto top-up is on). On an account with a monthly plan the response also carries subscription (the plan state, otherwise null) and topupBalanceHal (topped-up credit only); balanceHal stays the sum of monthly and topped-up credit, see the Monthly plan chapter.
Movement history
GET /v1/credit/ledger?limit - most recent first, WITHOUT a cursor (just limit, 1-200, default 50) - there is no cursor at all, so only the newest 200 movements are reachable through the API; the portal's /en/credit page and the tax documents have the full history. Every entry carries type (bonus, topup, call_out, call_in, sms, number_fee, agent, refund, admin, setup_revision - an extra revision round on a done-for-you setup order beyond what the package includes, subscription_grant - the monthly plan credit being credited and subscription_expire - its unused rest expiring), amountHal (positive for a credit, negative for a debit), grossHal (the VAT-inclusive amount for a card top-up, null otherwise) and an English description composed from the type - for number_fee, with the number the fee is for as well. A charge covered by the monthly credit also carries fromSubscriptionHal - the part of the charge the monthly credit paid; other movements do not have the field. The SMS number add-on shows up in the history as number_fee (the first period when you buy the number and every renewal after 30 days) and sms (a message sent from an SMS number, at the per-segment rate on the pricing page); when the carrier later rejects an SMS sent from an SMS number, the credit comes back as a refund entry.
Top-up by card
POST /v1/credit/topup {amountCzk} returns {checkoutUrl} - a Stripe Checkout link that a HUMAN opens in a browser. Nothing is charged until they complete the payment themselves. A complete billing address (PUT /v1/account/billing) is required BEFORE the call - without it a valid tax document can't be issued (billing_address_required). Supports Idempotency-Key - safe to retry after a timeout without creating two payments.
Auto top-up
Through the API, auto top-up can only be READ, TURNED OFF and have its THRESHOLD changed - turning it on and changing the AMOUNT go exclusively through Stripe Checkout, never a direct write.
Why not a plain "enabled: true"
When a card is already saved, a bare {"enabled": true} would trigger charging WITHOUT A HUMAN at the card - exactly what we want to avoid ("money only moves with a human at the card"). So PATCH /v1/credit/auto-topup rejects both enabled: true and amountHal with its own message, not a generic unknown-field error.
Reading and turning off
GET /v1/credit/auto-topup returns {enabled, thresholdHal, amountHal, card: {brand, last4} | null, spentThisMonthHal, monthlyCapHal, disabledReason} - WITHOUT the card number, just the brand and last four digits. PATCH {enabled: false} turns it off immediately, PATCH {thresholdHal} only changes the threshold (it must be one of the offered values and lower than the saved amount, otherwise the balance would drop back below the threshold right after a top-up).
Turning on and changing the amount
POST /v1/credit/auto-topup/setup {thresholdHal, amountHal} returns {checkoutUrl} - completing Checkout IMMEDIATELY charges amountHal as the first top-up, and only that saves the card for future auto top-ups. It works even with a card already saved from before - it's the only way to turn auto top-up on or change its amount. After Checkout, the browser lands back on the CZECH portal page (/kredit?automat=zapnuto) even for an English-speaking customer - a known gap, not a bug in your integration.
Card
POST /v1/credit/auto-topup/card returns a Checkout link (setup mode, nothing is charged) to replace the saved card. DELETE /v1/credit/auto-topup/card detaches the card IMMEDIATELY - the whole configuration is deleted (threshold and amount included), not just the card; turning it on again only works through .../setup, which charges a first top-up right away. After Checkout, the browser lands back on the CZECH portal page (/kredit?karta=zmenena) regardless of the account's language - same known gap as above.
Caps
Two INDEPENDENT caps: at most 10,000 CZK of credited amount per calendar month, and at most 4 top-ups per calendar DAY - no matter how many times the balance drops below the threshold. Exceeding either one turns auto top-up off automatically (disabledReason monthly_cap or daily_cap); spentThisMonthHal/monthlyCapHal in the response show how much of the monthly cap is left.
MCP: get_auto_topup (read) and disable_auto_topup (turn off) - turning it on, changing the amount and the card are not in MCP, for the same reason as enabled: true in REST above.
Monthly plan
An optional subscription: each month the price of the chosen tier is charged to the saved card and a monthly credit in CZK is credited, which is spent on the standard price list. Rates and per-second billing do not change, the plan is a layer on top of them. There are three tiers and tier is their price in CZK excluding VAT: "490", "990" and "1990"; the price and credit of each tier are returned by GET /v1/subscription in the tiers field.
When the offer is not available
The plan offer can be switched off for all accounts or only some. While it is off for your account, GET /v1/subscription returns available: false and turning it on fails with pausal_unavailable (404). A plan that is already on keeps running even if the offer is switched off.
Two kinds of credit
Monthly credit comes from the plan, expires at the end of the period and is non-refundable. Topped-up credit (top-ups, the welcome bonus, adjustments) does not expire and is refundable as before. The account balance - balanceHal in GET /v1/balance, in the portal and in every gate (minimum for a call, debt limit, warnings) - is the sum of both kinds. Topped-up credit alone is in topupBalanceHal, the remaining monthly credit in subscription.monthlyRemainingHal. A debt can only arise in topped-up credit.
Order of spending
Every charge is paid first from the monthly credit and the rest from topped-up credit - calls by the second, the agent, number and SMS number fees, SMS, gate rings and outbound task calls. The split is atomic and recorded in the ledger: a charge covered by the monthly credit carries fromSubscriptionHal (the part the monthly credit paid). Owner adjustments (admin, including negative ones) and the refund for an SMS that was not sent (refund) only touch topped-up credit.
Period and crediting
A period lasts one month from the successful charge (31 January gives 28 February). The monthly credit is credited only after the payment is confirmed, with a subscription_grant entry, and crediting is idempotent by payment id. If the account has a debt in topped-up credit at that moment, it is paid off from the new monthly credit first, so topped-up credit does not stay negative.
Expiry
The unused rest of the monthly credit expires on the renewal day (a subscription_expire entry with a negative amount), when the subscription ends, or by an automatic safety check when a period ended more than 36 hours ago without a renewal. If the charge fails, the rest has already expired and no new credit is added; until it is paid the account runs on topped-up credit only.
Choice after exhaustion
Turning the plan on requires an exhaustionMode, with no default. topup: once the monthly credit is used up, topped-up credit is bought from the saved card - it is today's auto top-up with the same limits and e-mails. cap: nothing is bought, inbound calls are still answered up to a debt of 50 CZK, and outbound calls, SMS and number purchases are refused with subscription_cap_reached (HTTP 402). Auto top-up cannot be turned on with a cap (subscription_cap_active); switching to the cap turns it off. The most you pay in a month with a cap is the price of the plan.
Changing tier, canceling, renewing
A tier change (up or down) takes effect from the next period, with no adjustment. Canceling is cancelAtPeriodEnd: true: the credit stays valid until the end of the period, then expires and the state becomes canceled; a cancellation can be undone until the end of the period. Over REST and MCP you cannot raise the tier, renew a canceled plan or switch to buying from the card - those steps raise or restart recurring charges, so they return subscription_confirmation_required with a confirmUrl that a human opens and confirms in the portal (valid for 24 hours).
Advice before renewal
Three days before renewal (at the latest one day before) an e-mail arrives, but only when paying by the second or a lower level would cost less at your spend so far. The same rule applies as for the advice in the renewal e-mail and in the portal: another option must be cheaper by at least 50 CZK and 5%. The estimate for the whole period uses the higher of the pace since the period started and over the last 7 days, and it is never lower than the last 30 days of spend the portal advises from, so after the click the Plan level and Cancel plan rows never answer that your level fits. The e-mail gives the exact date and time of the renewal and comes at most once per period, never for a plan canceled at the end of the period, with a scheduled level change, without a saved card or awaiting payment. Its button opens the Cancel plan row (/en/credit?pausal=zrusit) or the Plan level row (/en/credit?pausal=stupen) in the portal. The renewal e-mail then advises again from the actual spend of the whole period, so the advice may differ.
Unpaid renewal
The charge to the saved card is tried on the renewal day, after 2 days and after 4 days. After the first failure the state is past_due, an e-mail brings a link to pay manually and to change the card, and the monthly credit is 0; POST /v1/subscription/pay returns the manual payment link. After the third failure the plan ends (canceled) and topped-up credit and the account keep working.
VAT and tax documents
Prices exclude VAT. The VAT regime is read before every charge, so a payer from another EU country with a verified VAT ID pays without Czech VAT (reverse charge) and the document reflects it. volai itself issues a tax document for every paid period (the tax base is the tier price, not the credit that was added) and you find it in GET /v1/billing/documents.
Relation to the bonus and the first top-up
Turning the plan on counts as a first payment: it unlocks SMS from the shared sender, forwarding, a second number, an SMS number and the auto top-up card just like a top-up does. The welcome bonus stays topped-up credit.
Refunds
Monthly credit is non-refundable and expires; topped-up credit is refundable as before. Canceling the plan gives no claim to a prorated part of the paid period. Refunding a plan payment (a complaint, a mistake) is handled by the volai owner manually, not through the API or MCP.
The plan over REST and MCP
Money only moves with a human at the card. Turning the plan on and paying manually only return a Stripe Checkout link and never charge the card directly; raising the tier, resuming a plan set to end and switching to top-ups from the card need the confirmation of a signed-in human in the portal.
GET /v1/subscription- the plan state (subscriptionornull), the tier catalogtiers(id,priceHal,creditHal),topupBalanceHal,available,recommendation(the cheapest way to pay for the last 30 days of spend andsteady, whether the spend is ongoing rather than a one-off peak;nullwithout an offer or without 30 days of history) andpreviousPeriod(the last period with the same figures as the renewal e-mail:usedHalofcreditHal,usedPercent, spending beyond the monthly creditoverHaland what expiredexpiredHal; only while the plan is active,nullin the first period and when the credit history cannot prove the usage).POST /v1/subscription/checkout {tier, exhaustionMode, consumerConsent?}- returns a{checkoutUrl}for Stripe Checkout; the plan starts once it is completed.exhaustionMode(topuporcap) is required; a consumer without a company ID must sendconsumerConsent: true(otherwiseconsumer_consent_required). The billing address must be complete (billing_address_required). SupportsIdempotency-Key.PATCH /v1/subscription {tier?, cancelAtPeriodEnd?, exhaustionMode?}- lowering the tier, canceling (cancelAtPeriodEnd: true) and switching to the cap (exhaustionMode: "cap") are applied and return{subscription}. Raising the tier, resuming a plan set to end (cancelAtPeriodEnd: false) andexhaustionMode: "topup"change nothing and return 409subscription_confirmation_requiredwith aconfirmUrl.POST /v1/subscription/pay- only in thepast_duestate; returns a{checkoutUrl}to pay the renewal manually. SupportsIdempotency-Key.POST /v1/subscription/card- returns a{checkoutUrl}to replace the plan's saved card, nothing is charged. SupportsIdempotency-Key.GET /v1/balancealso carriessubscription(same shape as above, otherwisenull) andtopupBalanceHal.
MCP: get_subscription (read), create_subscription_link (a link to turn it on), update_subscription and cancel_subscription (lower the tier, cancel, cap; raising, renewing and buying from the card return a confirmUrl with a sentence that a human must confirm it in the portal) and create_subscription_payment_link (manual payment). There is no card replacement in MCP, for the same reason as with auto top-up: nothing is charged from here.
Tax documents
A document is issued automatically on a card top-up of credit (one-off or automatic), on paying for a done-for-you setup, and as a correction document on a setup refund - no manual step, no issuing through the API.
GET /v1/billing/documents?limit&before - most recent first, the before cursor (Unix ms, exclusive) is the nextBefore from the previous page. The underlying storage never returns more than 100 records per call, even when limit asks for more.
GET /v1/billing/documents/{id} returns {id, number, issuedAt, taxableAt, netHal, vatHal, grossHal, vatRatePercent, reverseCharge, description, customer, paymentRef, pdfUrl} - reverseCharge: true for payers from another EU country with a VIES-verified DIC, who pay without Czech VAT. The kind field is always filled in - "invoice" for a regular document (including ones issued before the field existed), "credit_note" for a correction document - issued automatically when the founder refunds a cancelled done-for-you setup order's fee, numbered in its own OD... series, with correctsNumber (the original document's number) and correctionReason (why it was refunded) added; netHal/vatHal/grossHal are POSITIVE even on a credit note, the minus sign is added only on the PDF.
GET /v1/billing/documents/{id}/pdf downloads the same document as application/pdf - it's rendered fresh on every download, not stored. A foreign or missing id reports the same error (invoice_not_found) on both endpoints, so the response can't reveal that someone else's document exists.
MCP: list_billing_documents and get_billing_document have the same shape as REST. The PDF is REST-only - MCP has no binary channel to transfer a file.
Errors
ledger_unavailable- The movement history or balance could not be read right now - try again shortly.invalid_amount- The top-up, threshold or auto top-up amount isn't one of the offered fixed values.billing_address_required- The billing address is missing or incomplete - add it in the portal (/en/settings) before topping up, otherwise a valid document can't be issued.stripe_not_configured- Payments are switched off for this volai deployment - not specific to your request.stripe_error- Stripe refused to create a payment session - try again shortly.autotopup_not_configured- Auto top-up has never been through.../setup- there is nothing to turn off, change or replace the card for.invoice_not_found- No document with thisidexists on the account (or it belongs to another account - the response is the same).subscription_cap_reached- The plan cap: monthly and topped-up credit are both used up and the account has chosen the cap. Outbound calls, SMS and number purchases are refused (HTTP 402) until the next renewal or a manual top-up.subscription_exists- The account already has a plan; a tier change, canceling and the choice after exhaustion go throughPATCH /v1/subscription.subscription_missing- The account has no active plan that could be changed, canceled or paid manually.subscription_canceled- The plan has already ended (statuscanceled): it cannot be changed, canceled or paid manually any more. Start a new one withPOST /v1/subscription/checkout.subscription_confirmation_required- Raising the tier, resuming a plan set to end and buying from the card are confirmed only by a human in the portal - the response carries aconfirmUrland nothing was changed.consumer_consent_required- A consumer without a company ID must confirm, when turning the plan on, an express request to start the service before the withdrawal period expires (consumerConsent: true).pausal_unavailable- The plan offer is currently switched off for this account.invalid_tier- The tier the plan already has, or the one it is already switching to; an unknown value outside"490","990","1990"ends asvalidation.subscription_cap_active- Auto top-up cannot be turned on with a cap; first switch the choice after exhaustion totopupin the portal.validation- Specifically onPATCH /v1/credit/auto-topup: an attempt to turn it on (enabled: true) or changeamountHaldirectly - usePOST .../setupinstead.
The full error table for every endpoint is in the Credit section of the REST API reference.
Related
MCP server
Connect Claude Code, Codex CLI, Codex desktop, Cursor and other AI editors.
REST API
Complete reference for every endpoint: numbers, calls, SMS, SMS numbers for receiving, agents and their drafts, tools, recordings, webhooks, do-not-call list, relay, account, tasks, credit, documents, calendars and integrations.
Tasks
Outbound campaigns: creation, CSV recipient import, start/pause/reconcile, budget and calling window.