API v2 (beta)
API v2 errors
Every API v2 error comes with a real HTTP status code and the same JSON shape. This page lists every code, what it means and what to do. Each error answer links to its code here in docs.
The error object#
{
"error": {
"code": "not_cancellable",
"message": "Only a waiting message can be cancelled.",
"field": null,
"docs": "https://wbiztool.com/docs/api/v2/errors/#not_cancellable",
"request_id": "req_4f1c2a9b8e7d6c5b4a392817",
"status": "sent"
}
}
| Field | Description |
|---|---|
code | What went wrong, one of the codes below. Codes don't change: branch on them in your code. |
message | An English explanation for the developer. It can change; don't parse it. |
field | The field the error is about, such as to.phone, to[3].phone or schedule.cron, or null. |
docs | A link to the code on this page. |
request_id | The request's id, also in the X-Request-Id header. Quote it if you contact us. |
errors | Only when a request has several problems: the others, each with field and message. |
Some codes add details of their own, such as status, count, used or limit; they're listed with the code. An answer with HTTP 401 also has a WWW-Authenticate: Bearer realm="wbiztool" header, and answers with 429 (and the 409 idempotency_in_progress) have Retry-After.
In short: 4xx means the request needs changing (or, for 409 and 429, waiting); retrying it unchanged gives the same answer. 5xx means our side; retry later, with the same Idempotency-Key.
Authentication and limits#
api_v2_not_enabled#
HTTP 403. API v2 isn't available for this workspace. If you see this, contact us.
What to do: keep using API v1 for now. The API v2 keys card on Settings → API keys shows when it's open for you.
missing_api_key#
HTTP 401. The request has no Authorization: Bearer … header, or it's empty or uses another scheme.
What to do: send Authorization: Bearer wbz_live_… (or wbz_test_…) on every request. See Keys.
invalid_api_key#
HTTP 401. The key isn't a WbizTool v2 key: mistyped, cut short, or not one we made.
What to do: copy the key again, without spaces or quotes. A key can't be shown again once made; if you've lost it, make a new one on Settings → API keys.
v1_key_on_v2#
HTTP 401. The key is an API v1 key (40 characters, digits and a to f). v1 keys work only on /api/v1/.
What to do: make a v2 key in the API v2 keys card on Settings → API keys, or call API v1.
api_key_revoked#
HTTP 401. The key was revoked on Settings → API keys, or its workspace was deleted or disabled.
What to do: use another key, or ask a workspace Owner to make a new one.
api_key_expired#
HTTP 401. The key's expiry date has passed.
What to do: ask a workspace Owner to make a new key, and replace the old one in your app.
too_many_auth_failures#
HTTP 429. More than 30 requests in a minute from your IP address had a wrong key. Retry-After is 60.
What to do: stop and fix the key in your app, then try again after a minute.
demo_account#
HTTP 403. A demo workspace can't use the API.
What to do: sign up for your own account and make a key there.
missing_scope#
HTTP 403. The key doesn't have the scope this request needs. required_scope in the error says which, such as messages:send.
What to do: make a key with that scope (scopes can't be added to an existing key). See Scopes.
rate_limited#
HTTP 429. The key has made more than 60 requests this minute, or more than 10,000 today (UTC).
What to do: wait the number of seconds in Retry-After, then try again. Spread requests out, use POST /messages/batch for many recipients, and webhooks instead of polling. See Rate limits.
Request#
not_found#
HTTP 404. There's nothing at this address: the path is mistyped, or isn't an API v2 path.
What to do: check the path against the reference, for example /api/v2/messages, not /api/v2/message.
invalid_json#
HTTP 400. The body isn't valid JSON, often because of a trailing comma or an unescaped line break in text.
What to do: check the JSON, and write new lines in strings as \n. Let your HTTP library build the body (json= in Python requests, JSON.stringify in JavaScript).
unsupported_content_type#
HTTP 415. The body isn't sent the way this endpoint takes it: every endpoint takes application/json, except POST /media, which takes multipart/form-data.
What to do: set Content-Type: application/json and send JSON, or send the upload as multipart with the field file.
method_not_allowed#
HTTP 405. The path exists but not with this method, such as PUT /messages/9817263.
What to do: use the method the reference shows. A change to a reminder is PATCH; cancelling a message is DELETE.
request_too_large#
HTTP 413. The request body is too large.
What to do: send less in one request, for example split a long list into several batches.
validation_failed#
HTTP 400. The request doesn't have the right shape: a required field is missing, a value has the wrong type or is too long, a field isn't known, or limit isn't 1 to 100. field names the first problem and message explains it; errors lists any others.
What to do: fix the field named in field. Field names are snake_case and unknown fields are refused, so check for typos such as txt or phone_number.
invalid_cursor#
HTTP 400. The cursor is damaged, or was made for other filters than this request has.
What to do: send exactly the next_cursor you got, with the same filters as the first page. To change the filters, start again without a cursor.
invalid_idempotency_key#
HTTP 400. The Idempotency-Key header is empty, longer than 255 characters, or has characters that can't be printed.
What to do: use a UUID or another short string of letters, digits and dashes.
idempotency_key_reused#
HTTP 422. This Idempotency-Key was already used in the last 24 hours with a different request: another method, path or body.
What to do: use a new key for each new request, and reuse a key only to retry exactly the same request. See Idempotency-Key.
idempotency_in_progress#
HTTP 409. The first request with this Idempotency-Key is still being handled. Retry-After is 1.
What to do: wait a second and send the same request again: you'll get the first request's answer.
internal_error#
HTTP 500. Something went wrong on our side. We're told about it.
What to do: retry later with the same Idempotency-Key, so the work isn't done twice. If it keeps happening, contact us with the request_id.
Messages#
text_required#
HTTP 400. The message has no text and no media, or the text is only spaces. For a reminder, text is required.
What to do: send text, media or both.
text_too_long#
HTTP 400. text is longer than 3,000 characters.
What to do: shorten it, or split it into several messages.
too_many_recipients#
HTTP 400. A batch has more than 1,000 recipients in to.
What to do: split the list into batches of up to 1,000, each with its own Idempotency-Key.
invalid_datetime#
HTTP 400. A time (send_at, expires_at, since, until) isn't ISO 8601 with its offset.
What to do: write it like 2026-10-20T09:30:00+05:30 or 2026-10-20T04:00:00Z. A time without an offset, such as 2026-10-20 09:30, is refused because it's ambiguous. In a query string, write + as %2B.
invalid_phone#
HTTP 422. A phone number (to.phone, or phone when connecting a number) isn't a valid number in international format with +.
What to do: write it with + and the country code, such as +919876543210. Spaces and dashes are fine; a leading 0 instead of the country code isn't.
invalid_username#
HTTP 422. to.username isn't a Telegram username.
What to do: write @name, a t.me/name link or tg: and a user id. See Recipients.
username_needs_telegram#
HTTP 422. A Telegram username can only be sent to from a Telegram account, and from is a WhatsApp number (or, without from, there isn't exactly one Telegram account connected).
What to do: set from to a Telegram account's id, or send to a phone number instead.
contact_not_found#
HTTP 422. No contact with this to.contact_id in the workspace.
What to do: check the id (the contact_id of an earlier message, say), or send to to.phone instead.
media_not_found#
HTTP 422 when sending a message or making a reminder with media.media_id; HTTP 404 for GET or DELETE /media/{id}. There's no file with this id in the workspace's Media directory, or it was deleted.
What to do: check the id with List files, or upload the file again.
invalid_media_url#
HTTP 422. media.url isn't a public http or https link: it's malformed, or points to a private or local address.
What to do: use a link anyone on the internet can open, or upload the file and send it by media_id.
time_in_past#
HTTP 422. send_at or expires_at has already passed.
What to do: check the time and its offset. To send now, leave send_at out.
number_not_found#
HTTP 422 when from (or number_id when connecting) isn't a number of this workspace; HTTP 404 for GET /numbers/{id}, also when the key may not use that number.
What to do: get the right id from List numbers.
from_required#
HTTP 422. from is missing, and the workspace has no connected number or more than one, so it isn't clear which should send. (For a key limited to some numbers: the only connected number isn't one of its numbers.)
What to do: send from with the id of the number to send from.
number_not_allowed#
HTTP 403. The key is limited to some numbers, and this request needs another one: from is another number, a reminder doesn't name one of its numbers, a connect request adds a number or names another one, or the request manages webhook endpoints.
What to do: use one of the key's numbers (number_ids in GET /me), or a key that isn't limited.
no_credits#
HTTP 402. The workspace has no messages left on its plan. For a batch, requested and remaining say how many were asked for and how many are left.
What to do: send fewer messages, or buy a plan or more messages; paid plans send unlimited messages. See When you run out of messages.
account_disabled#
HTTP 403. The account of the workspace's owner is disabled, so the workspace can't send.
What to do: contact support.
number_daily_cap_reached#
HTTP 429. The sending number has sent 1,000 messages in the last 24 hours (fair use), or a batch wouldn't fit in what's left: requested and room say how many.
What to do: send the rest later, or from another number.
message_rejected#
HTTP 422. The message couldn't be queued; message says why. Nothing was stored.
What to do: fix what message describes and send again.
message_not_found#
HTTP 404. No message with this id, for this key: it doesn't exist, belongs to another workspace, or was sent from a number the key may not use. A test key sees only its own test messages, and a live key never sees them.
What to do: check the id, and that you use the same kind of key (live or test) that made the message.
not_cancellable#
HTTP 409. Only a waiting message (queued or scheduled) can be cancelled, and this one isn't waiting any more. status in the error says what it is now, such as sent or cancelled.
What to do: nothing to cancel. A message that has been sent can't be recalled.
Reminders#
invalid_cron#
HTTP 400. schedule.cron isn't a 5-part cron expression, or describes a time that never comes (such as 31 February).
What to do: write minute, hour, day of the month, month and day of the week, such as 0 9 * * 1 for Mondays at 09:00. See Schedule.
invalid_timezone#
HTTP 400. schedule.timezone isn't an IANA time zone name.
What to do: use a name such as Asia/Kolkata, Europe/London or America/New_York, not an abbreviation like IST or an offset like +05:30. See the time zone reference.
number_not_connected#
HTTP 422. The reminder's from isn't a connected number of this workspace.
What to do: connect the number first, choose a connected one, or leave from out (null) so any connected number sends it.
reminder_not_found#
HTTP 404. No reminder with this id: it doesn't exist, was deleted, or sends from a number the key may not use.
What to do: check the id with List reminders.
Media#
file_too_large#
HTTP 413. The uploaded file is larger than 64 MB.
What to do: compress it or split it; for a video, lower its resolution.
unsupported_file_type#
HTTP 415. The file's type can't be sent on WhatsApp. The type is read from the extension at the end of the file name.
What to do: check the types you can upload, and keep the extension in the name (price-list.pdf, not price-list).
storage_full#
HTTP 422. The file doesn't fit in the workspace's Media directory for its plan. used and limit say how much is used and allowed, in bytes.
What to do: delete files you no longer need, or move to a plan with more storage.
media_in_use#
HTTP 409. The file can't be deleted yet: something will still send it (a waiting message, including a campaign's, an active reminder or an active Google Sheets trigger). count says how many.
What to do: cancel or change those first, or wait until the waiting messages have gone out.
upload_failed#
HTTP 502. The file couldn't be stored on our side.
What to do: try again, with the same Idempotency-Key.
Number checks#
no_valid_numbers#
HTTP 422. None of the entries in numbers is a phone number. skipped says how many entries there were.
What to do: send numbers in international format with +, one per entry, such as +919876543210.
too_many_numbers#
HTTP 422. The check has more numbers than one check may have: over 10,000 entries in the request, or more numbers than the plan allows per check. count and limit say how many.
What to do: split the list into several checks. See Plan limits.
number_check_quota_reached#
HTTP 422. The check would go over the plan's number checks for this billing period. requested, used and limit say how many.
What to do: check fewer numbers, wait for the next billing period, or upgrade the plan.
number_check_not_found#
HTTP 404. No number check with this id in the workspace.
What to do: check the id with List checks.
Numbers#
plan_required#
HTTP 403. The workspace's plan doesn't include what this request needs: the QR code API to connect a number, reminders to create, change or resume a reminder, or number verification to start a number check.
What to do: an Owner can upgrade on Pricing. Reading what's already there works on any plan.
number_limit_reached#
HTTP 403. The plan has no room for another number.
What to do: reconnect an existing number with number_id, remove a number you no longer use, or upgrade the plan.
already_connected#
HTTP 409. The number is connected already.
What to do: nothing to do: you can send from it. Check with Get a number.
connect_in_progress#
HTTP 409. This number is being connected right now. number_id in the error says which.
What to do: poll GET /numbers/{id} for that number instead of asking again. After 10 minutes without a scan, you can ask again.
Webhooks#
invalid_webhook_url#
HTTP 422. url isn't a public https:// address: it's http://, malformed, or points to a private or local address.
What to do: use an https:// address your server answers on from the internet. For testing on your computer, use a tunnel service that gives you a public https:// address.
webhook_limit_reached#
HTTP 403. The workspace already has 5 webhook endpoints.
What to do: remove one you no longer use. One endpoint can get every event type.
webhook_not_found#
HTTP 404. No webhook endpoint with this id: it doesn't exist or was removed. Testing or rotating the secret of a stopped endpoint answers this too; reading it shows active: false.
What to do: check the id with List endpoints. If it stopped, add it again.
Test mode#
test_mode_unsupported#
HTTP 403. A test key (wbz_test_…) can send, read and cancel test messages and read the rest of the workspace, but not change anything else: connect a number, create, change or delete reminders, upload or delete media, start a number check, or manage webhook endpoints. The status filter of GET /messages isn't supported for test keys either.
What to do: use a live key (wbz_live_…) for these requests. See Test mode.
