Error Codes
Every Voicebip API error returns a consistent JSON envelope:
1 { 2 "error_code": "NOT_FOUND", 3 "message": "Agent agt_WXYZ_abc123def456 not found", 4 "request_id": "req_k7m2n9p4q1", 5 "documentation_url": "https://docs.voicebip.com/errors/NOT_FOUND" 6 }
| Field | Type | Description |
|---|---|---|
error_code | string | Machine-readable error code (use this for programmatic handling) |
message | string | Human-readable description of what went wrong |
request_id | string | Unique request identifier — include this when contacting support |
documentation_url | string | Direct link to troubleshooting guidance for this error |
Error Code Reference
Authentication & Authorization
| Error Code | HTTP Status | Description | Common Cause |
|---|---|---|---|
UNAUTHENTICATED | 401 | Missing or invalid API key | No Authorization header, malformed key, or key has been revoked |
PERMISSION_DENIED | 403 | Valid key but insufficient permissions | Attempting to access a resource in another workspace (RLS violation) |
RATE_LIMITED | 429 | Too many requests | Exceeded your tier’s per-minute request limit. Check the Retry-After header |
Resource Errors
| Error Code | HTTP Status | Description | Common Cause |
|---|---|---|---|
NOT_FOUND | 404 | Resource does not exist | Wrong ID, resource was deleted, or it belongs to a different workspace |
ALREADY_EXISTS | 409 | Duplicate resource | Creating an agent or key with a label that already exists in the workspace |
INVALID_ARGUMENT | 400 | Request validation failed | Missing required field, invalid E.164 phone number, unsupported enum value |
Quota & Billing
| Error Code | HTTP Status | Description | Common Cause |
|---|---|---|---|
QUOTA_EXCEEDED | 402 | Tier limit reached | Hit your plan’s agent count, voice minute, SMS, or WhatsApp conversation limit. Upgrade your tier or wait for the billing cycle to reset |
WORKSPACE_BLOCKED | 402 | Workspace has no balance — all billable operations are suspended | Balance reached zero. Top up via POST /v1/billing/checkout to unblock immediately |
INSUFFICIENT_BALANCE | 402 | Account balance is too low for the requested operation | Upgrading to a higher plan requires a prorated charge. Top up first, then retry the plan change |
TIER_SWITCH_TOO_FREQUENT | 429 | Plan changes are limited to once every 24 hours | A plan change was already made in the last 24 hours. Wait until the cooldown expires, then retry |
Subscription
| Error Code | HTTP Status | Description | Common Cause |
|---|---|---|---|
NO_ACTIVE_SUBSCRIPTION | 400 | Workspace has no active subscription | Attempt to switch plans on a workspace that has never subscribed. Start a subscription first |
NO_OP | 400 | Requested plan matches the current plan | No change was made |
TIER_SWITCH_UNSUPPORTED | 400 | Plan switching is not available for this workspace type | PAYG workspaces cannot use tier switching |
WORKSPACE_NOT_FOUND | 404 | Workspace does not exist | Workspace was deleted, or the request is missing a valid workspace context |
INVALID_PLAN | 400 | Unrecognized plan name | Valid plans are starter, builder, and scale |
Telephony & Messaging
| Error Code | HTTP Status | Description | Common Cause |
|---|---|---|---|
NUMBER_UNAVAILABLE | 409 | Number cannot be provisioned | Number was claimed by another workspace between listing and provisioning (race condition). Retry with a different number |
CHANNEL_MISMATCH | 409 | Number does not support the requested channel | Trying to assign a voice-only DID to a WhatsApp channel, or vice versa |
CALL_FAILED | 502 | Voice call could not be established | All MNO routes failed (MTN, Glo, Airtel, 9mobile failover exhausted) or destination unreachable |
CALL_WINDOW_RESTRICTED | 422 | Call blocked by the agent’s call-window configuration | The call was attempted outside the agent’s configured operating hours. CBN compliance requirement for financial institutions |
MESSAGE_FAILED | 502 | SMS or WhatsApp message delivery failed | SMPP session down, MNO rejected the message, or WhatsApp conversation window expired |
OPT_OUT_ENFORCED | 422 | Recipient has opted out of messages from this workspace | Recipient previously sent STOP/UNSUBSCRIBE. Sending to opted-out numbers is blocked |
WEBHOOK_FAILED | 502 | Webhook delivery failed | Your webhook endpoint returned a non-2xx status or timed out. Check delivery logs |
Infrastructure
| Error Code | HTTP Status | Description | Common Cause |
|---|---|---|---|
AI_PROVIDER_ERROR | 502 | AI provider returned an error or timed out | Gemini or OpenAI is down. Circuit breaker may have tripped — the system will auto-failover to the secondary provider after 30 seconds |
UNAVAILABLE | 503 | Service temporarily unavailable | Internal service is restarting or under heavy load. Retry with exponential backoff |
INTERNAL | 500 | Unexpected server error | A bug on our side. Please report with the request_id so we can investigate |
Handling Errors in Code
cURL
$ curl -s -w "\n%{http_code}" -X GET "https://api.voicebip.com/v1/agents/agt_invalid" \ > -H "Authorization: Bearer pk_live_your_key"
TypeScript (with @voicebip/sdk)
1 import { VoicebipClient, VoicebipError } from "@voicebip/sdk"; 2 3 const client = new VoicebipClient({ apiKey: "pk_live_your_key" }); 4 5 try { 6 const agent = await client.agents.get("agt_invalid"); 7 } catch (err) { 8 if (err instanceof VoicebipError) { 9 console.error(`[${err.errorCode}] ${err.message}`); 10 // Handle specific codes 11 if (err.errorCode === "NOT_FOUND") { 12 // Agent doesn't exist 13 } else if (err.errorCode === "RATE_LIMITED") { 14 // Back off and retry 15 } 16 } 17 }
Go
1 resp, err := http.Get("https://api.voicebip.com/v1/agents/agt_invalid") 2 if err != nil { 3 log.Fatal(err) 4 } 5 defer resp.Body.Close() 6 7 if resp.StatusCode != http.StatusOK { 8 var apiErr struct { 9 ErrorCode string `json:"error_code"` 10 Message string `json:"message"` 11 RequestID string `json:"request_id"` 12 } 13 json.NewDecoder(resp.Body).Decode(&apiErr) 14 log.Printf("[%s] %s (request: %s)", apiErr.ErrorCode, apiErr.Message, apiErr.RequestID) 15 }
Python
1 import requests 2 3 resp = requests.get( 4 "https://api.voicebip.com/v1/agents/agt_invalid", 5 headers={"Authorization": "Bearer pk_live_your_key"}, 6 ) 7 8 if not resp.ok: 9 error = resp.json() 10 print(f"[{error['error_code']}] {error['message']}") 11 if error["error_code"] == "RATE_LIMITED": 12 retry_after = int(resp.headers.get("Retry-After", 10)) 13 time.sleep(retry_after)
Retry Strategy
Not all errors are retryable. Use this as a guide:
| Error Code | Retryable | Strategy |
|---|---|---|
UNAUTHENTICATED | No | Fix your API key |
PERMISSION_DENIED | No | Check workspace ownership |
INVALID_ARGUMENT | No | Fix the request payload |
NOT_FOUND | No | Verify the resource ID |
ALREADY_EXISTS | No | Use a different identifier |
RATE_LIMITED | Yes | Wait for Retry-After seconds, then retry |
QUOTA_EXCEEDED | No | Upgrade tier or wait for billing cycle reset |
WORKSPACE_BLOCKED | No | Top up your balance, then retry |
INSUFFICIENT_BALANCE | No | Top up your balance, then retry the plan change |
TIER_SWITCH_TOO_FREQUENT | Yes | Wait 24 hours from your last plan change, then retry |
NO_ACTIVE_SUBSCRIPTION | No | Start a subscription first |
NO_OP | No | No action needed — already on the requested plan |
TIER_SWITCH_UNSUPPORTED | No | Not applicable for PAYG workspaces |
WORKSPACE_NOT_FOUND | No | Verify workspace credentials |
INVALID_PLAN | No | Use starter, builder, or scale |
NUMBER_UNAVAILABLE | Yes | Retry with a different number from the available pool |
CHANNEL_MISMATCH | No | Choose a number that supports the target channel |
CALL_WINDOW_RESTRICTED | No | Retry during the agent’s configured operating hours |
OPT_OUT_ENFORCED | No | Recipient has opted out — do not retry |
CALL_FAILED | Yes | Retry after a short delay (MNO may recover) |
MESSAGE_FAILED | Yes | Retry with exponential backoff |
WEBHOOK_FAILED | Auto | Voicebip retries automatically: 0s, 1m, 5m, 15m, 1h, 4h, 24h (7 attempts) |
AI_PROVIDER_ERROR | Yes | Retry after 30s (circuit breaker cooldown) |
UNAVAILABLE | Yes | Exponential backoff starting at 1s |
INTERNAL | Yes | Retry once, then report with request_id |