Billing, quota and wallet
CallQA never advances money: a call is only placed when your quota or your prepaid wallet can cover it.
Plans
| Plan | Price | Test calls per month | Extra call (from the wallet) | Seats | Phone countries | Concurrent calls |
|---|---|---|---|---|---|---|
| Starter | $99/mo | 100 | $0.75 | 3 | 1 | 2 |
| Growth | $499/mo | 750 | $0.55 | 10 | 5 | 5 |
| Scale | $1,499/mo | 3,000 | $0.40 | unlimited | all | 15 |
Annual billing is 20% cheaper ($79, $399 and $1,199 per month, billed yearly). Every plan
includes scheduled tests, the API, CI/CD webhooks and alerts. Growth adds auto-derived test
coverage, custom test data injection, version tagging and comparison (Dashboard > Versions
and environments), and advanced analytics (90 days of history; Starter keeps 7 and 30
days). Scale adds SSO/SAML, custom reporting (Monitoring > Reports) and the advanced
scenario builder. GET /me returns your plan and its feature flags; GET /plans lists the
public plans with their prices.
A feature your plan does not include answers 403 with code: feature_not_in_plan,
required_plan and upgrade_url in the API, and shows a locked card with an Upgrade
button in the dashboard. A limit (seats, phone countries, concurrent calls) names the cheapest
plan that lifts it. See Errors.
Free trial
A new organization comes with 10 free test calls to try CallQA, no card needed. They are
bonus calls: no time limit, used before any wallet credit, and the ones left are kept when
you choose a plan. The trial has 1 phone country, 2 concurrent calls, no paid overage and the
Starter features. Once the 10 calls are used, new calls end with block_reason:
quota_exhausted until an admin chooses a plan in Billing. GET /usage shows what is
left in bonus_remaining (plan: "trial").
The trial goes to the first organization a person creates, once: not to a second organization, nor to one created again after deleting the first. An early access invitation gives the free report (50 calls) instead.
Subscribe or change plan
An admin subscribes in Billing (Monthly or Annual under a plan), or with an admin key:
curl -sS -X POST https://app.callqa.dev/api/v1/billing/subscription/checkout \
-H "Authorization: Bearer $CALLQA_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{"plan": "growth", "interval": "annual"}' | jq -r .url
Open the returned Stripe Checkout URL to pay. The subscription becomes active when Stripe
confirms it (GET /billing/subscription shows the plan, status, interval, the current
period, cancel_at_period_end and, after a failed renewal, payment_retry_until).
Once subscribed, change the plan or the interval, update the card, cancel, or download invoices
in the Stripe billing portal: Billing > Invoices and payment method, or
POST /billing/portal, which returns the portal URL. A second checkout answers 409
(subscription_exists), also while the subscription waits for a failed payment (update the
card in the portal) and for an organization invoiced manually. The plan buttons of the
Billing page open the portal's confirmation screen once you are subscribed. success_url, cancel_url and return_url are optional and must use
https.
When a renewal payment fails
If the card is declined when your subscription renews:
- Stripe emails the billing contact that the payment failed, with a link to update the card, and again at each failed retry.
- Stripe retries the payment several times, at the moments most likely to succeed, for up to about 2 weeks.
- During these retries the subscription is
past_dueand your plan keeps working: test calls, schedules, CI runs and the API go on as usual, within your quota. Every page of the dashboard shows a banner with the deadline; admins can update the card from it (or in Billing > Invoices and payment method, orPOST /billing/portal).GET /billing/subscriptionreturnspayment_retry_until, the same deadline. - Once the card is updated, Stripe retries at once; when it succeeds, the banner goes away and nothing else changes.
- If the last retry fails, Stripe cancels the subscription. The organization loses its
plan: new calls end with
block_reason: subscription_inactiveuntil you subscribe again in Billing.
The grace period covers the plan only. Calls beyond the monthly quota are still paid from the prepaid wallet: CallQA does not advance money during the retries either.
Wallet credit when the subscription ends
Wallet credit is only used for calls, so it is of no use once the subscription is canceled
(by you, or after failed payments) or the account is closed. Unused credit is refunded on
request: an admin writes to us from their account email, and we refund it to the card used
for the top-ups. The refund shows as a refund line in GET /wallet/transactions. See
section 8.2 of the Terms of Service.
Units
The quota counts units. A call costs:
units = weight x ceil(duration / 10 minutes)
- A call of up to 10 minutes to a weight 1 destination costs 1 unit. Every started 10-minute slice counts as one more. The duration is what the carrier bills, from answer to hangup: ringing time does not count.
- The weight depends on the destination. US and Canada: 1. Other fixed lines, toll free and VoIP: 1. Mobile numbers outside the US and Canada cost more to reach and weigh 2 or 3 (for example 2 for French mobiles, 3 for UK mobiles).
- A call lasts at most
limits.max_duration_s(5 minutes by default), and never more than your plan's maximum call duration (10 minutes).
Units are taken, in this order, from:
- your monthly quota (calendar month, UTC);
- your bonus calls (free trial, free report, referrals, one-time grants; they have no time limit);
- your prepaid wallet, at your plan's extra call price per unit.
Reserve, then capture
Before dialing, CallQA reserves the worst case: the call's maximum duration. After the
call it captures what was really used and releases the rest. A call that ends in error,
is canceled or is never dialed releases its whole reservation. A call that is dialed but not
answered still counts at least one slice.
GET /usageshowsused,held(reserved by calls in progress),remaining,bonus_remainingandwallet_available_cents.GET /walletshowsbalance_cents,held_centsandavailable_cents(available = balance - held).GET /wallet/transactionsis the append-only ledger: every change of the balance has a line (topup,hold,release,capture,manual_credit,manual_debit,refund).
Each call reports what it cost: quota_units and cost_cents (the wallet part only, 0 while
you are within quota). Each run sums cost_cents.
Top up the wallet
In the dashboard (Billing), or with an admin key:
curl -sS -X POST https://app.callqa.dev/api/v1/wallet/checkout \
-H "Authorization: Bearer $CALLQA_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{"amount_cents": 5000}' | jq -r .url
Open the returned Stripe Checkout URL to pay. The wallet is credited when Stripe confirms the
payment, not before. Top-ups are between $20 and $5,000: amount_cents from 2000 to
500000 (422 outside this range). success_url and cancel_url are optional.
Prices and top-ups are before tax: VAT or sales tax is added at checkout where it applies,
and the wallet is credited the amount before tax. A refunded top-up takes its credit back
from the wallet (in proportion for a partial refund), never more than what is left: the
ledger shows a refund line.
Overage: on or off, monthly cap, block or pause
Calls beyond the monthly quota and the bonus calls are paid overage, taken from the
wallet. An admin decides, in Billing > Overage or with PATCH /billing/settings:
allow_paid_overage(on by default): off, calls stop once the quota and bonus calls are used, even with credit in the wallet (block reasonoverage_disabled).monthly_spend_cap_cents: the most the wallet may spend in a calendar month (UTC), reservations of calls in progress included. A call that would go over it is not placed (block reasonspend_cap_reached). No cap by default.on_limit: what happens when a limit stops calls.block_calls(default): each call ends asblocked_insufficient_creditwith the reason.pause_schedules: the calls are blocked too, and the organization's schedules are paused so scheduled runs stop piling up blocked calls. They resume on the 1st of next month (UTC), or at once when an admin turns overage on, raises or removes the cap, or upgrades the plan.
The first time a limit stops calls in a month, the admins get an email and the audit log a
billing.limit_reached line. Nothing is charged for calls that were not placed. Changing
these settings needs the admin role (billing.overage); with an API key, the admin
scope.
With an admin key:
curl -sS -X PATCH https://app.callqa.dev/api/v1/billing/settings \
-H "Authorization: Bearer $CALLQA_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{"monthly_spend_cap_cents": 50000}'
{ "monthly_spend_cap_cents": 50000, "spent_this_month_cents": 12650, "low_balance_threshold_cents": 2000,
"allow_paid_overage": true, "on_limit": "block_calls", "schedules_paused": false }
GET /billing/settings reads the same object. Only the fields you send change; null removes
the cap. Every change is recorded in the audit log.
Low balance warning
When a charge takes the wallet balance under a threshold, CallQA sends the
wallet.low_balance webhook once. It is sent again only after a top-up brought
the balance back to the threshold or above, and never twice within 6 hours. The threshold is
$20 by default; change it with low_balance_threshold_cents in PATCH /billing/settings, or
set it to null to turn the event off.
When credit runs out
A call that neither the quota, nor bonus calls, nor the wallet can cover is not placed. It ends immediately with:
{
"status": "blocked_insufficient_credit",
"block_reason": "insufficient_credit",
"error": "This call needs $0.55 of prepaid credit ($0.10 available). Top up your prepaid wallet (Billing > Prepaid wallet, or POST /api/v1/wallet/checkout) or upgrade your plan. Calls beyond the monthly quota are paid from the wallet."
}
block_reason |
Meaning | Fix |
|---|---|---|
insufficient_credit |
Quota used up and the wallet cannot cover the reservation | Top up the wallet |
quota_exhausted |
Quota used up and your plan has no pay-as-you-go option (or the 10 calls of the free trial are used) | Upgrade the plan (choose one after the free trial) |
spend_cap_reached |
Your monthly wallet spending cap is reached | An admin raises it (PATCH /billing/settings) |
overage_disabled |
Quota and bonus calls used up, and paid overage is off | An admin turns it on, or upgrades |
subscription_inactive |
No active subscription (status blocked_policy): none yet, or canceled after the failed payment retries |
Choose a plan in Billing (during the retries the plan keeps working: see When a renewal payment fails) |
A blocked call counts in the run's blocked counter, makes the run verdict fail, and fails
any CI gate. It also sends the call.blocked webhook. Find them with
GET /calls?status=blocked_insufficient_credit.
The trigger request itself still succeeds (201): credit is checked per call, when the call
is about to be dialed, not when the run is created.
Forecast of the month
Billing and the Dashboard show a gauge of the month: units already used or reserved, plus what your schedules will use until the quota resets (the 1st, 00:00 UTC), against your quota and bonus calls. When the projection goes over, an alert says how many units of paid overage it means and what they cost from the wallet, or how many calls will be blocked (overage off, no overage on the plan, or the monthly cap).
A scenario has one schedule per environment where it is enabled (see Environments). For each enabled schedule the forecast counts:
- the runs left this month, from its cron expression in its time zone (a run at 23:30 in Los Angeles on the 30th is already next month in UTC, so next month's quota);
- the test cases per run (enabled variants, 1 without variants);
- the destination weight of the number dialed in that environment;
- the average 10 minute slices of the scenario's calls in that environment over the last 30 days (in any environment when it never ran there), or its maximum duration when it never ran at all (a conservative estimate).
The answer lists each schedule (schedules) and the total per environment
(environments).
Manual, API and CI runs are not planned, so they are not in the forecast. With a read key:
curl -sS -H "Authorization: Bearer $CALLQA_API_KEY" https://app.callqa.dev/api/v1/billing/forecast \
| jq '{projected_units, included, will_exceed, estimated_overage_cents, blocked_units}'
Referral: both get 50 test calls
Your referral link is on the Billing page (and GET /billing/referral). A team that opens
it and creates a new organization is linked to yours. When that organization starts its
first paid subscription, both organizations get 50 bonus calls, used after the monthly quota
and before the wallet. Signing up alone gives nothing.
No bonus when the new organization is created by a member of yours, pays with the same Stripe customer as yours, or is created by someone who already belongs to another organization. At most 20 rewarded referrals per organization per year.
Check before a big run
curl -sS -H "Authorization: Bearer $CALLQA_API_KEY" https://app.callqa.dev/api/v1/usage \
| jq '{remaining, bonus_remaining, wallet_available_cents, overage_price_cents}'
GET /usage and GET /plans need the read scope. GET /wallet, the ledger, top-ups, the
subscription, the portal and the billing settings need admin.