> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.voicebip.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.voicebip.com/_mcp/server.

# Error Codes

> Complete reference of Voicebip API error codes, HTTP status mappings, and troubleshooting guidance.

Every Voicebip API error returns a consistent JSON envelope:

```json
{
  "error_code": "NOT_FOUND",
  "message": "Agent agt_WXYZ_abc123def456 not found",
  "request_id": "req_k7m2n9p4q1",
  "documentation_url": "https://docs.voicebip.com/errors/NOT_FOUND"
}
```

| 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](/api-reference/webhooks/list-deliveries) |

### Infrastructure

| Error Code          | HTTP Status | Description                                | Common Cause                                                                                                                                |
| ------------------- | ----------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `AI_PROVIDER_ERROR` | 502         | AI provider returned an error or timed out | The hosted AI provider 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

```bash
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)

```typescript
import { VoicebipClient, VoicebipError } from "@voicebip/sdk";

const client = new VoicebipClient({ apiKey: "pk_live_your_key" });

try {
  const agent = await client.agents.get("agt_invalid");
} catch (err) {
  if (err instanceof VoicebipError) {
    console.error(`[${err.errorCode}] ${err.message}`);
    // Handle specific codes
    if (err.errorCode === "NOT_FOUND") {
      // Agent doesn't exist
    } else if (err.errorCode === "RATE_LIMITED") {
      // Back off and retry
    }
  }
}
```

### Go

```go
resp, err := http.Get("https://api.voicebip.com/v1/agents/agt_invalid")
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
    var apiErr struct {
        ErrorCode string `json:"error_code"`
        Message   string `json:"message"`
        RequestID string `json:"request_id"`
    }
    json.NewDecoder(resp.Body).Decode(&apiErr)
    log.Printf("[%s] %s (request: %s)", apiErr.ErrorCode, apiErr.Message, apiErr.RequestID)
}
```

### Python

```python
import requests

resp = requests.get(
    "https://api.voicebip.com/v1/agents/agt_invalid",
    headers={"Authorization": "Bearer pk_live_your_key"},
)

if not resp.ok:
    error = resp.json()
    print(f"[{error['error_code']}] {error['message']}")
    if error["error_code"] == "RATE_LIMITED":
        retry_after = int(resp.headers.get("Retry-After", 10))
        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`                                 |