> ## Documentation Index
> Fetch the complete documentation index at: https://docs.poly.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Error codes

> HTTP status codes and platform-specific error codes returned by PolyAI APIs.

When a request fails, PolyAI APIs return a structured JSON error response. This page documents the HTTP status codes, error response format, and platform-specific error codes you may encounter.

## Error response format

All API errors return a JSON body with the following structure:

```json  theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "success": false,
  "error": "Request body failed schema validation.",
  "error_id": "550e8400-e29b-41d4-a716-446655440000",
  "error_code": "SCHEMA_VALIDATION_FAILED",
  "error_message": "Request body failed schema validation.",
  "data": null
}
```

| Field           | Type    | Description                                                                                                |
| --------------- | ------- | ---------------------------------------------------------------------------------------------------------- |
| `success`       | boolean | Always `false` for error responses.                                                                        |
| `error`         | string  | Human-readable error message.                                                                              |
| `error_id`      | string  | Unique request identifier from the `X-PolyAI-Correlation-Id` header. Include this when contacting support. |
| `error_code`    | string  | Machine-readable error code in `UPPER_SNAKE_CASE`.                                                         |
| `error_message` | string  | Human-readable error message.                                                                              |
| `data`          | null    | Always `null` for error responses.                                                                         |

## Standard HTTP status codes

These standard codes apply across all PolyAI APIs.

| Status | Meaning                | When it occurs                                                                           |
| ------ | ---------------------- | ---------------------------------------------------------------------------------------- |
| 400    | Bad request            | The request body or query parameters failed validation.                                  |
| 401    | Unauthorized           | Missing, expired, or invalid API key or token.                                           |
| 403    | Forbidden              | You do not have permission for the requested action.                                     |
| 404    | Not found              | The requested resource does not exist.                                                   |
| 405    | Method not allowed     | The HTTP method is not supported for this endpoint.                                      |
| 408    | Request timeout        | The request exceeded the server timeout.                                                 |
| 409    | Conflict               | A resource with the same identifier already exists.                                      |
| 410    | Gone                   | The resource has been permanently deleted.                                               |
| 412    | Precondition failed    | A required precondition was not met (for example, deploying to live before pre-release). |
| 415    | Unsupported media type | The uploaded file type is not supported.                                                 |
| 422    | Unprocessable entity   | The request is well-formed but contains semantic errors.                                 |
| 429    | Too many requests      | Rate limit exceeded. Retry after the period indicated in the response headers.           |
| 500    | Internal server error  | An unexpected error occurred on the server.                                              |
| 501    | Not implemented        | The requested functionality is not yet available.                                        |
| 502    | Bad gateway            | An upstream service returned an error or timed out.                                      |
| 503    | Service unavailable    | The service is temporarily unavailable.                                                  |

## Platform error codes

PolyAI APIs return domain-specific error codes alongside HTTP status codes. There are 63 of them across 13 categories — search by code, ID, or keyword below, or filter by HTTP status, instead of scanning tables.

export const ERROR_CODES = [{
  code: "AUTH_USER_NOT_FOUND",
  id: "3001",
  http: 401,
  category: "Authentication and authorization",
  description: "User not found in the auth system."
}, {
  code: "AUTH_USER_UNAUTHORISED",
  id: "3002",
  http: 401,
  category: "Authentication and authorization",
  description: "Invalid credentials or expired session."
}, {
  code: "AUTH_USER_INVALID_TOKEN",
  id: "3003",
  http: 401,
  category: "Authentication and authorization",
  description: "Token is malformed, expired, or revoked."
}, {
  code: "USER_ACCESS_FORBIDDEN",
  id: "2",
  http: 403,
  category: "Authentication and authorization",
  description: "User lacks permission for the requested action."
}, {
  code: "EXTERNAL_USER_FORBIDDEN",
  id: "3",
  http: 403,
  category: "Authentication and authorization",
  description: "External user privilege level is too low."
}, {
  code: "ACCOUNT_INSUFFICIENT_PERMISSIONS",
  id: "1003",
  http: 403,
  category: "Authentication and authorization",
  description: "User lacks permissions for this account operation."
}, {
  code: "CONVERSATIONS_NOT_FOUND",
  id: "5001",
  http: 404,
  category: "Conversations",
  description: "Conversation ID does not exist."
}, {
  code: "CONVERSATIONS_VALIDATION_FAILED",
  id: "5002",
  http: 400,
  category: "Conversations",
  description: "Conversation request body failed validation."
}, {
  code: "CONVERSATIONS_RECORDING_NOT_FOUND",
  id: "5003",
  http: 404,
  category: "Conversations",
  description: "Audio recording not found for this conversation."
}, {
  code: "GET_CONVERSATIONS_FILTER_PARSE_ERROR",
  id: "—",
  http: 400,
  category: "Conversations",
  description: "Filter expression syntax is invalid."
}, {
  code: "GET_CONVERSATIONS_INVALID_SORT_PARAMETER",
  id: "—",
  http: 400,
  category: "Conversations",
  description: "Sort field is not in the allowed set."
}, {
  code: "CHAT_DRAFT_NOT_EXIST",
  id: "4001",
  http: 400,
  category: "Chat",
  description: "The draft you are attempting to chat with does not exist."
}, {
  code: "CHAT_TIMEOUT",
  id: "4002",
  http: 400,
  category: "Chat",
  description: "Chat session exceeded the timeout limit."
}, {
  code: "CHAT_DRAFT_DEPLOYMENT_FAILED",
  id: "4003",
  http: 500,
  category: "Chat",
  description: "Draft auto-deploy failed before the chat could start."
}, {
  code: "ChatConversationEnded",
  id: "—",
  http: 410,
  category: "Chat",
  description: "You sent a message to a conversation that has already ended."
}, {
  code: "DEPLOYMENT_NOT_FOUND",
  id: "6001",
  http: 404,
  category: "Deployments",
  description: "Deployment ID does not exist."
}, {
  code: "DEPLOYMENTS_ALREADY_PUBLISHED",
  id: "6009",
  http: 409,
  category: "Deployments",
  description: "Draft is already published to the target environment."
}, {
  code: "DEPLOYMENTS_ENVIRONMENT_ALREADY_PUBLISHED",
  id: "6002",
  http: 200,
  category: "Deployments",
  description: "Environment already has this version (idempotent)."
}, {
  code: "DEPLOYMENTS_VALIDATION_FAILED",
  id: "6007",
  http: 400,
  category: "Deployments",
  description: "Request body validation failed."
}, {
  code: "DEPLOYMENTS_INVALID_ENVIRONMENT",
  id: "6008",
  http: 400,
  category: "Deployments",
  description: "Invalid or missing `environment` query parameter."
}, {
  code: "ErrPreReleaseNotReady",
  id: "—",
  http: 412,
  category: "Deployments",
  description: "You must deploy to pre-release before promoting to live."
}, {
  code: "FLOW_NOT_FOUND",
  id: "8001",
  http: 404,
  category: "Flows",
  description: "Flow ID does not exist."
}, {
  code: "FLOWS_NAME_ALREADY_EXISTS",
  id: "8003",
  http: 409,
  category: "Flows",
  description: "A flow with this name already exists."
}, {
  code: "FLOWS_FUNCTION_NAME_ALREADY_EXISTS",
  id: "8004",
  http: 409,
  category: "Flows",
  description: "A function with this name already exists in the flow."
}, {
  code: "FLOWS_RESERVED_PARAMETER_NAME",
  id: "8005",
  http: 400,
  category: "Flows",
  description: "You used a reserved parameter name."
}, {
  code: "FLOWS_RESERVED_FUNCTION_NAME",
  id: "8006",
  http: 400,
  category: "Flows",
  description: "You used a reserved function name."
}, {
  code: "FUNCTION_NOT_FOUND",
  id: "9001",
  http: 404,
  category: "Functions",
  description: "Function ID does not exist."
}, {
  code: "FUNCTIONS_DEPLOYMENT_HAS_ERRORS",
  id: "9004",
  http: 400,
  category: "Functions",
  description: "Function code contains errors that prevent deployment."
}, {
  code: "FUNCTION_EXECUTION_FAILED",
  id: "9006",
  http: 400,
  category: "Functions",
  description: "Function encountered a runtime execution error."
}, {
  code: "FUNCTION_FAILED_TO_PARSE",
  id: "9007",
  http: 400,
  category: "Functions",
  description: "Function source code has parse errors."
}, {
  code: "FUNCTION_NAME_ALREADY_EXISTS",
  id: "9009",
  http: 409,
  category: "Functions",
  description: "A function with this name already exists."
}, {
  code: "FUNCTIONS_RESERVED_NAME",
  id: "9010",
  http: 400,
  category: "Functions",
  description: "You used a system-reserved function name."
}, {
  code: "KNOWLEDGE_BASE_TOPIC_ALREADY_EXISTS",
  id: "11001",
  http: 409,
  category: "Knowledge base",
  description: "A topic with this name already exists."
}, {
  code: "KNOWLEDGE_BASE_IMPORT_NO_CSV_FOUND",
  id: "11002",
  http: 400,
  category: "Knowledge base",
  description: "No CSV file found in the import request."
}, {
  code: "KNOWLEDGE_BASE_IMPORT_INVALID_TYPE",
  id: "11003",
  http: 415,
  category: "Knowledge base",
  description: "The uploaded file type is not supported."
}, {
  code: "KNOWLEDGE_BASE_IMPORT_MISSING_COLUMNS",
  id: "11004",
  http: 400,
  category: "Knowledge base",
  description: "Required columns are missing from the CSV import."
}, {
  code: "KNOWLEDGE_BASE_TOPICS_INVALID",
  id: "11005",
  http: 400,
  category: "Knowledge base",
  description: "Topic data failed validation."
}, {
  code: "KNOWLEDGE_BASE_RICH_TEXT_INVALID",
  id: "11006",
  http: 400,
  category: "Knowledge base",
  description: "Rich text markup is malformed or references a missing entity."
}, {
  code: "PHONE_NUMBERS_NOT_FOUND",
  id: "14006",
  http: 404,
  category: "Phone numbers and connectors",
  description: "Phone number does not exist."
}, {
  code: "PHONE_NUMBERS_ALREADY_EXISTS",
  id: "14007",
  http: 409,
  category: "Phone numbers and connectors",
  description: "Phone number has already been imported."
}, {
  code: "PHONE_NUMBERS_INVALID_PHONE_NUMBER_FORMAT",
  id: "14003",
  http: 400,
  category: "Phone numbers and connectors",
  description: "Number is not in valid E.164 format."
}, {
  code: "PHONE_NUMBERS_CONNECTOR_DOES_NOT_EXIST",
  id: "14005",
  http: 404,
  category: "Phone numbers and connectors",
  description: "Referenced connector not found."
}, {
  code: "CONNECTORS_NOT_FOUND",
  id: "14100",
  http: 404,
  category: "Phone numbers and connectors",
  description: "Connector ID does not exist."
}, {
  code: "CONNECTORS_VALIDATION_FAILED",
  id: "14101",
  http: 400,
  category: "Phone numbers and connectors",
  description: "Connector request body failed validation."
}, {
  code: "VARIANTS_BAD_ATTRIBUTES_ERROR",
  id: "26001",
  http: 400,
  category: "Variants",
  description: "Attribute values do not match the expected schema."
}, {
  code: "VARIANTS_DUPLICATE_NAME_ERROR",
  id: "26002",
  http: 409,
  category: "Variants",
  description: "A variant with this name already exists."
}, {
  code: "VARIANTS_REMOVE_DEFAULT_ERROR",
  id: "26003",
  http: 400,
  category: "Variants",
  description: "You cannot remove the default variant."
}, {
  code: "VariantsImportInvalidDelimiter",
  id: "—",
  http: 400,
  category: "Variants",
  description: "CSV delimiter not recognized."
}, {
  code: "VariantImportMissingColumnsHttp",
  id: "—",
  http: 400,
  category: "Variants",
  description: "CSV is missing required columns."
}, {
  code: "VariantImportDuplicateColumnsHttp",
  id: "—",
  http: 400,
  category: "Variants",
  description: "CSV has duplicate column headers."
}, {
  code: "REAL_TIME_CONFIG_FORBIDDEN_ACCESS",
  id: "29001",
  http: 403,
  category: "Real-time configuration",
  description: "Not authorized to access real-time configs."
}, {
  code: "REAL_TIME_CONFIG_INVALID_FOR_SCHEMA",
  id: "29002",
  http: 400,
  category: "Real-time configuration",
  description: "Config values fail JSON Schema validation."
}, {
  code: "REAL_TIME_CONFIG_INVALID_SCHEMA",
  id: "29004",
  http: 400,
  category: "Real-time configuration",
  description: "JSON Schema definition is invalid."
}, {
  code: "REAL_TIME_CONFIG_UNKNOWN_CLIENT_ENVIRONMENT",
  id: "29006",
  http: 400,
  category: "Real-time configuration",
  description: "Client environment is not one of `sandbox`, `pre-release`, or `live`."
}, {
  code: "ACCOUNT_NOT_FOUND",
  id: "1002",
  http: 404,
  category: "Accounts and projects",
  description: "Account ID does not exist."
}, {
  code: "ACCOUNT_CREATE_INVALID_ID",
  id: "1004",
  http: 400,
  category: "Accounts and projects",
  description: "Account ID contains non-alphanumeric characters or is too similar to an existing ID."
}, {
  code: "ACCOUNT_CREATE_EXISTING_ID",
  id: "1006",
  http: 409,
  category: "Accounts and projects",
  description: "Account ID is already taken."
}, {
  code: "PROJECT_NOT_FOUND",
  id: "15001",
  http: 404,
  category: "Accounts and projects",
  description: "Project ID does not exist."
}, {
  code: "PROJECT_ID_ALREADY_EXISTS",
  id: "15005",
  http: 409,
  category: "Accounts and projects",
  description: "Project ID is already taken."
}, {
  code: "PROJECT_ID_INVALID",
  id: "15006",
  http: 400,
  category: "Accounts and projects",
  description: "Project ID does not meet format requirements."
}, {
  code: "SMS_TEMPLATE_NOT_FOUND",
  id: "20001",
  http: 404,
  category: "SMS",
  description: "SMS template ID does not exist."
}, {
  code: "MissingTestCasesHttp",
  id: "—",
  http: 422,
  category: "Test suite",
  description: "One or more test case IDs were not found."
}, {
  code: "NoTestCasesHttp",
  id: "—",
  http: 422,
  category: "Test suite",
  description: "No test cases exist for this project."
}];


export const ERROR_CATEGORIES = ["Authentication and authorization", "Conversations", "Chat", "Deployments", "Flows", "Functions", "Knowledge base", "Phone numbers and connectors", "Variants", "Real-time configuration", "Accounts and projects", "SMS", "Test suite"];


export const ERROR_HTTP_STATUSES = [200, 400, 401, 403, 404, 409, 410, 412, 415, 422, 500];


export const errorStatusColor = http => {
  if (http < 300) return {
    bg: "rgba(74, 124, 16, 0.1)",
    fg: "#3a6b08",
    border: "rgba(74, 124, 16, 0.35)"
  };
  if (http < 500) return {
    bg: "rgba(180, 90, 30, 0.1)",
    fg: "#8a4517",
    border: "rgba(180, 90, 30, 0.35)"
  };
  return {
    bg: "rgba(190, 40, 40, 0.1)",
    fg: "#a02020",
    border: "rgba(190, 40, 40, 0.35)"
  };
};


export const renderErrorInline = text => {
  const parts = text.split("`");
  return parts.map((part, i) => i % 2 === 1 ? <code key={i} style={{
    fontSize: "0.85em"
  }}>{part}</code> : part);
};


// Clipboard API unavailable or blocked -- still show feedback below, just don't copy.
export const ErrorCodeExplorer = () => {
  const [query, setQuery] = useState("");
  const [activeStatuses, setActiveStatuses] = useState([]);
  const [copied, setCopied] = useState("");
  const toggleStatus = status => {
    setActiveStatuses(prev => prev.includes(status) ? prev.filter(s => s !== status) : [...prev, status]);
  };
  const copyCode = code => {
    try {
      if (typeof navigator !== "undefined" && navigator.clipboard && navigator.clipboard.writeText) {
        navigator.clipboard.writeText(code).catch(() => {});
      }
    } catch (err) {}
    setCopied(code);
    setTimeout(() => setCopied(""), 1500);
  };
  const q = query.trim().toLowerCase();
  const filtered = ERROR_CODES.filter(e => {
    const matchesQuery = !q || e.code.toLowerCase().includes(q) || e.id.toLowerCase().includes(q) || e.description.toLowerCase().includes(q) || String(e.http).includes(q);
    const matchesStatus = activeStatuses.length === 0 || activeStatuses.includes(e.http);
    return matchesQuery && matchesStatus;
  });
  const grouped = ERROR_CATEGORIES.map(category => ({
    category,
    rows: filtered.filter(e => e.category === category)
  })).filter(g => g.rows.length > 0);
  return <div style={{
    margin: "1.5rem 0",
    maxWidth: "100%",
    boxSizing: "border-box"
  }}>
      <div style={{
    display: "flex",
    flexWrap: "wrap",
    gap: "0.75rem",
    alignItems: "center",
    marginBottom: "0.9rem"
  }}>
        <input type="text" value={query} onChange={e => setQuery(e.target.value)} placeholder="Search by code, ID, or description..." style={{
    flex: "1 1 260px",
    padding: "0.6rem 0.9rem",
    borderRadius: "10px",
    border: "1px solid rgba(0,0,0,0.15)",
    fontSize: "0.9rem",
    outline: "none",
    boxSizing: "border-box"
  }} />
        {(query || activeStatuses.length > 0) && <button type="button" onClick={() => {
    setQuery("");
    setActiveStatuses([]);
  }} style={{
    padding: "0.55rem 0.9rem",
    borderRadius: "10px",
    border: "1px solid rgba(0,0,0,0.15)",
    background: "transparent",
    fontSize: "0.85rem",
    cursor: "pointer",
    color: "#4b5563"
  }}>
            Clear
          </button>}
      </div>

      <div style={{
    display: "flex",
    flexWrap: "wrap",
    gap: "0.4rem",
    marginBottom: "0.9rem"
  }}>
        {ERROR_HTTP_STATUSES.map(status => {
    const active = activeStatuses.includes(status);
    const c = errorStatusColor(status);
    return <button key={status} type="button" onClick={() => toggleStatus(status)} style={{
      padding: "0.25rem 0.65rem",
      borderRadius: "999px",
      border: `1px solid ${active ? c.fg : c.border}`,
      background: active ? c.fg : c.bg,
      color: active ? "#fff" : c.fg,
      fontSize: "0.78rem",
      fontWeight: 600,
      cursor: "pointer"
    }}>
              {status}
            </button>;
  })}
      </div>

      <div style={{
    fontSize: "0.82rem",
    color: "#6b7280",
    marginBottom: "0.6rem"
  }}>
        Showing {filtered.length} of {ERROR_CODES.length} error codes
      </div>

      {filtered.length === 0 ? <div style={{
    padding: "2rem 1.5rem",
    textAlign: "center",
    borderRadius: "12px",
    border: "1px dashed rgba(0,0,0,0.15)",
    color: "#6b7280",
    fontSize: "0.9rem"
  }}>
          No error codes match "{query}". Try a different search term or clear the status filters.
        </div> : grouped.map(group => <div key={group.category} style={{
    marginBottom: "1.5rem"
  }}>
            <h4 style={{
    fontSize: "0.95rem",
    margin: "0 0 0.5rem",
    color: "#374151"
  }}>{group.category}</h4>
            <div style={{
    display: "flex",
    flexDirection: "column",
    gap: "0.4rem"
  }}>
              {group.rows.map(row => {
    const c = errorStatusColor(row.http);
    return <div key={row.code} style={{
      display: "flex",
      flexWrap: "wrap",
      alignItems: "center",
      gap: "0.4rem 0.6rem",
      padding: "0.55rem 0.7rem",
      borderRadius: "10px",
      border: "1px solid rgba(0,0,0,0.08)",
      fontSize: "0.88rem",
      maxWidth: "100%",
      boxSizing: "border-box"
    }}>
                    <button type="button" onClick={() => copyCode(row.code)} title="Copy error code" style={{
      fontFamily: "monospace",
      fontSize: "0.82rem",
      background: "rgba(74, 124, 16, 0.06)",
      border: "none",
      borderRadius: "6px",
      padding: "0.2rem 0.45rem",
      cursor: "pointer",
      color: "#3a6b08",
      maxWidth: "100%",
      minWidth: 0,
      overflowWrap: "anywhere",
      wordBreak: "break-word",
      whiteSpace: "normal",
      textAlign: "left"
    }}>
                      {copied === row.code ? "Copied!" : row.code}
                    </button>
                    <span style={{
      padding: "0.1rem 0.5rem",
      borderRadius: "999px",
      background: c.bg,
      color: c.fg,
      fontSize: "0.78rem",
      fontWeight: 600,
      flexShrink: 0
    }}>{row.http}</span>
                    {row.id !== "—" && <span style={{
      color: "#9ca3af",
      fontSize: "0.78rem",
      flexShrink: 0
    }}>ID {row.id}</span>}
                    <span style={{
      flexBasis: "100%",
      minWidth: 0,
      overflowWrap: "anywhere",
      color: "#374151"
    }}>{renderErrorInline(row.description)}</span>
                  </div>;
  })}
            </div>
          </div>)}
    </div>;
};


<ErrorCodeExplorer />

## WebSocket close codes

If you are using the WebRTC Gateway or webchat WebSocket connections, you may encounter these close codes:

| Code | Meaning          | When it occurs                                                        |
| ---- | ---------------- | --------------------------------------------------------------------- |
| 1000 | Normal closure   | Connection closed cleanly.                                            |
| 1006 | Abnormal closure | Connection dropped unexpectedly (for example, network failure).       |
| 408  | Pong timeout     | Server did not receive a pong response within the configured timeout. |

## Troubleshooting

### Include the correlation ID in support requests

Every API response includes an `X-PolyAI-Correlation-Id` header. When contacting PolyAI support, include this value so the team can trace the request through the system.

### Common patterns

| Symptom                  | Likely cause                 | Resolution                                                                                     |
| ------------------------ | ---------------------------- | ---------------------------------------------------------------------------------------------- |
| 401 on every request     | API key missing or malformed | Verify the `x-api-key` header is present and correctly formatted.                              |
| 403 after key rotation   | Old key revoked              | Confirm you are using the new key.                                                             |
| 404 for a known resource | Wrong region                 | Verify the base URL matches your account region.                                               |
| 409 on create            | Duplicate identifier         | Use a different name or ID, or check for existing resources.                                   |
| 422 on import            | Schema mismatch              | Verify CSV columns match the expected format. Check the error `data` field for column details. |

## Related pages

<CardGroup cols={2}>
  <Card title="API getting started" icon="key" href="/api-reference/introduction">
    Authentication, base URLs, and API versioning.
  </Card>

  <Card title="Conversations API" icon="messages" href="/api-reference/conversations/introduction">
    Retrieve conversation data and transcripts.
  </Card>
</CardGroup>
