Errors and limits
The error format, status codes and error codes returned by the voicast API, and its rate, size and time limits.
Error format#
json
{
"error": "invalid_config",
"message": "config に誤りがあります",
"details": ["fields[0].key は英小文字・数字・_(先頭は英字)", "closing は空でない文字列"],
"request_id": "req_…"
}- Branch on
error(the error code).messageis a human-readable explanation, currently in Japanese, and its wording may change. - On validation errors (422),
detailslists one entry per problem. request_ididentifies the request. Every response, successful or not, also has it inx-request-id. Quote it when contacting support.
Status codes#
| HTTP | Meaning |
|---|---|
| 400 | The body is not JSON |
| 401 | The API key is missing, wrong or revoked |
| 402 | New calls are refused for billing reasons |
| 403 | Not allowed |
| 404 | Not found (including resources in another environment) |
| 409 | Conflicts with the current state (duplicate, in use and so on) |
| 422 | Invalid input |
| 429 | Rate limit exceeded |
| 500 | An internal error in voicast |
| 503 | An external service (payments, a connected provider, webhook delivery) is unreachable or not configured |
Error codes#
| HTTP | error |
Meaning | What to do |
|---|---|---|---|
| 400 | invalid_json |
The body is not a JSON object | Send a JSON object with Content-Type: application/json |
| 401 | unauthorized |
The API key is missing, wrong or revoked | Check Authorization: Bearer vk_… |
| 402 | balance_depleted |
The balance is zero or below (prepaid) | Top up, or turn on auto-recharge |
| 402 | payment_overdue |
14 days have passed since an invoice was due (invoice billing) | Pay the invoice (service resumes immediately) |
| 402 | spending_cap_reached |
This month's estimate reached the spending cap (invoice billing) | Raise the cap in the dashboard |
| 403 | forbidden |
Your role is not enough, or MCP was called from a browser | Ask an owner or admin for a higher role |
| 404 | not_found |
No such ID, or it belongs to another environment | Check the ID with a list call; make sure you are not mixing test and live keys |
| 409 | conflict |
A tenant with the same external_id exists |
Use the existing tenant |
| 409 | in_use |
Tenants still use this agent | Change their agent_id first |
| 409 | tenant_disabled |
The tenant is disabled | Set status: active with PATCH /v1/tenants/{id} |
| 409 | needs_reauth |
The connection is broken | Connect it again on the connect page |
| 422 | invalid_request |
Invalid input | See message and details |
| 422 | invalid_config |
Invalid agent or connection config | Fix the items in details |
| 422 | no_agent |
No agent could be determined | Pass agent_id or set one on the tenant |
| 422 | verify_failed |
The test call before saving a connection failed | Check the URL and API key |
| 422 | connect_failed |
A connection could not be started (for example, an MCP server's metadata could not be read) | Check the URL and what the server supports |
| 429 | rate_limited |
Rate limit exceeded | Wait a moment and retry |
| 500 | internal |
An internal error | Wait a moment and retry. Contact us if it persists |
| 503 | unavailable |
An external service is unreachable or not configured | Wait a moment and retry |
When calls are refused#
When new calls are refused for billing reasons (402), each channel sees it differently. Calls already in progress are never cut off.
| Channel | What you see |
|---|---|
| Web and apps | POST /v1/sessions returns 402. If the refusal starts between creating the session and connecting, the offer to webrtc_url returns 403 (this will become 402; treat both as "cannot connect") |
| Phone | The voice server closes the stream immediately and Twilio moves on to action or the next verb. No call record is created |
Rate limits#
| Applies to | Limit |
|---|---|
The /v1 API and MCP with an API key |
600 requests per minute per API key (counted together) |
| MCP (signed in) | 300 calls per minute per user |
| MCP client registration | 30 per hour per source address |
| Sign-in emails | 5 per hour per email address |
/v1 responses carry x-ratelimit-limit (the per-minute limit), x-ratelimit-remaining and x-ratelimit-reset (end of the window, Unix seconds). Over the limit you get 429 rate_limited with Retry-After (seconds to wait); wait that long and retry (the SDK does this for you).
Size and time limits#
| Item | Limit |
|---|---|
client_token (Web and apps) |
2 minutes, single use |
| Connect-page and webhook-portal links | 15 minutes |
| After rotating a connection secret or the tools signing secret | The old one keeps working for 24 hours |
| Tool responses | voicast waits 8 seconds; up to 4,000 characters of JSON reach the AI |
| Connection actions | Cut off after 3 seconds |
| Agents | 20 fields, 50 FAQ entries, 10 tools, 10 connections (Agents) |
GET /v1/calls |
Up to 100 per page (default 50) |
GET /v1/usage |
Periods up to 366 days |
| Transcripts | Up to 500 turns, 2,000 characters each |
Tenant external_id |
1–100 characters: letters, digits and . _ : - |
| Names (agents, tenants) | Up to 100 characters |