{
  "openapi": "3.1.0",
  "info": {
    "title": "router.africa API",
    "version": "1.0.0",
    "summary": "One OpenAI-compatible API for many models, paid in credits.",
    "description": "The public API at `https://api.router.africa`. This file is the curated\ncontract: it lists only what is supported. Anything not listed here is not\npart of the API, even if it appears to work.\n\nAmounts of money are **credits**, always as decimal strings with eight\ndecimal places and no currency code.\n\nEvery response, success or error, carries an `x-request-id` header. Quote\nit when you contact support.\n"
  },
  "servers": [
    {
      "url": "https://api.router.africa",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Chat"
    },
    {
      "name": "Models"
    },
    {
      "name": "Messages"
    },
    {
      "name": "Responses"
    },
    {
      "name": "Generation"
    },
    {
      "name": "Key"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/v1/chat/completions": {
      "post": {
        "tags": [
          "Chat"
        ],
        "operationId": "createChatCompletion",
        "summary": "Create a chat completion",
        "description": "OpenAI-compatible chat completions. Set `stream: true` to receive\nserver-sent events.\n\nOnly one choice is returned: `n`, if sent, must be `1`.\n\nAn error before the first token is an ordinary HTTP error. An error\nafter streaming has started cannot change the `200`; it is sent as a\nfinal event, `data: {\"error\": {...}}` (same envelope as every other\nerror), and the stream ends.\n",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "router.africa extension, not part of OpenAI's API. A client-chosen\nstring (for example a UUID). A repeat of a finished request with\nthe same key and the same API key returns the first result instead\nof running (and charging) again. A repeat while the first is still\nrunning returns `409` `request_in_progress`. Without this header an\nidentical body is treated the same way.\n",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatCompletionRequest"
              },
              "example": {
                "model": "openai/gpt-4.1-mini",
                "messages": [
                  {
                    "role": "user",
                    "content": "Habari! Say hello in Swahili."
                  }
                ],
                "max_tokens": 100
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The completion, or a stream of chunks when `stream` is true.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "x-idempotent-replay": {
                "$ref": "#/components/headers/IdempotentReplay"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatCompletion"
                },
                "example": {
                  "id": "chatcmpl-0192f0e0",
                  "object": "chat.completion",
                  "created": 1767225600,
                  "model": "openai/gpt-4.1-mini",
                  "choices": [
                    {
                      "index": 0,
                      "message": {
                        "role": "assistant",
                        "content": "Habari!"
                      },
                      "finish_reason": "stop"
                    }
                  ],
                  "usage": {
                    "prompt_tokens": 12,
                    "completion_tokens": 3,
                    "total_tokens": 15
                  }
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Server-sent events. Each `data:` line is one JSON\n`ChatCompletionChunk` (or, last, an error envelope), then\n`data: [DONE]`.\n"
                },
                "x-event-schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ChatCompletionChunk"
                    },
                    {
                      "$ref": "#/components/schemas/Error"
                    }
                  ]
                },
                "example": "data: {\"id\":\"chatcmpl-0192f0e0\",\"object\":\"chat.completion.chunk\",\"created\":1767225600,\"model\":\"openai/gpt-4.1-mini\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"Habari\"},\"finish_reason\":null}]}\n\ndata: {\"id\":\"chatcmpl-0192f0e0\",\"object\":\"chat.completion.chunk\",\"created\":1767225600,\"model\":\"openai/gpt-4.1-mini\",\"choices\":[{\"index\":0,\"delta\":{},\"finish_reason\":\"stop\"}],\"usage\":{\"prompt_tokens\":12,\"completion_tokens\":3,\"total_tokens\":15}}\n\ndata: [DONE]\n"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "408": {
            "$ref": "#/components/responses/RequestTimeout"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "529": {
            "$ref": "#/components/responses/ProviderOverloaded"
          }
        }
      }
    },
    "/v1/models": {
      "get": {
        "tags": [
          "Models"
        ],
        "operationId": "listModels",
        "summary": "List models",
        "description": "The models this API key can call.",
        "responses": {
          "200": {
            "description": "The key's models.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelList"
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "openai/gpt-4.1-mini",
                      "object": "model",
                      "created": 1767225600,
                      "owned_by": "openai"
                    },
                    {
                      "id": "anthropic/claude-sonnet-4.5",
                      "object": "model",
                      "created": 1767225600,
                      "owned_by": "anthropic"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/key": {
      "get": {
        "tags": [
          "Key"
        ],
        "operationId": "getKey",
        "summary": "Get the current key",
        "description": "The calling key's spend limit, usage and expiry, and the workspace's\navailable credits. All amounts are credits.\n",
        "responses": {
          "200": {
            "description": "The key and its workspace balance.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyInfo"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/messages": {
      "post": {
        "tags": [
          "Messages"
        ],
        "operationId": "createMessage",
        "security": [
          {
            "anthropicApiKey": []
          },
          {
            "bearerAuth": []
          }
        ],
        "summary": "Create a message (Anthropic format)",
        "description": "Anthropic Messages-compatible endpoint: point the Anthropic SDK's base URL\nat `https://api.router.africa` and keep your code. Set `stream: true` to\nreceive Anthropic-style server-sent events.\n\nAuthenticate with `x-api-key: YOUR_KEY` (what the Anthropic SDK sends) or\n`Authorization: Bearer YOUR_KEY`. This is the only endpoint that accepts\n`x-api-key`.\n\nErrors use Anthropic's error format, not the envelope of the other\nendpoints: `{\"type\": \"error\", \"error\": {\"type\": \"...\", \"message\": \"...\",\n\"reason\": \"...\"}}`. `error.type` is Anthropic's type; `error.reason` is\nour specific cause (see the Errors page). An error before the first token\nis an ordinary HTTP error. An error after streaming has started cannot\nchange the `200`; it is sent as a final `event: error` event and the\nstream ends.\n\nNot supported: a `fallbacks` array and `models` (fallback chains are\nconfigured by router.africa, not per request).\n",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "router.africa extension. Works exactly as on `POST /v1/chat/completions`:\na repeat of a finished request with the same key and the same API key\nreturns the first result (marked `x-idempotent-replay: true`, not charged\nagain); a repeat while the first is still running returns `409`\n`request_in_progress`. Without this header an identical body is treated\nthe same way.\n",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessageRequest"
              },
              "example": {
                "model": "anthropic/claude-sonnet-4.5",
                "max_tokens": 100,
                "messages": [
                  {
                    "role": "user",
                    "content": "Habari! Say hello in Swahili."
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The message, or a stream of events when `stream` is true.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "x-idempotent-replay": {
                "$ref": "#/components/headers/IdempotentReplay"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                },
                "example": {
                  "id": "msg_0192f0e0",
                  "type": "message",
                  "role": "assistant",
                  "model": "anthropic/claude-sonnet-4.5",
                  "content": [
                    {
                      "type": "text",
                      "text": "Habari!"
                    }
                  ],
                  "stop_reason": "end_turn",
                  "stop_sequence": null,
                  "usage": {
                    "input_tokens": 12,
                    "output_tokens": 3
                  }
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Server-sent events in Anthropic's format: each event has an\n`event:` line naming its type and a `data:` line with one JSON\n`MessageStreamEvent` (or, last, an `event: error` event carrying an `AnthropicError`). There is no\n`[DONE]` line: the stream ends after `message_stop`.\n"
                },
                "x-event-schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/MessageStreamEvent"
                    },
                    {
                      "$ref": "#/components/schemas/AnthropicError"
                    }
                  ]
                },
                "example": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_0192f0e0\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"anthropic/claude-sonnet-4.5\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":12,\"output_tokens\":1}}}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Habari!\"}}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0}\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"output_tokens\":3}}\n\nevent: message_stop\ndata: {\"type\":\"message_stop\"}\n\n(an error after streaming has started instead ends the stream with:)\nevent: error\ndata: {\"type\":\"error\",\"error\":{\"type\":\"overloaded_error\",\"message\":\"The model provider is overloaded. Retry shortly.\",\"reason\":\"provider_overloaded\"}}\n"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/AnthropicBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/AnthropicUnauthorized"
          },
          "402": {
            "$ref": "#/components/responses/AnthropicPaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/AnthropicForbidden"
          },
          "404": {
            "$ref": "#/components/responses/AnthropicNotFound"
          },
          "408": {
            "$ref": "#/components/responses/AnthropicRequestTimeout"
          },
          "409": {
            "$ref": "#/components/responses/AnthropicConflict"
          },
          "413": {
            "$ref": "#/components/responses/AnthropicPayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/AnthropicTooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/AnthropicInternalError"
          },
          "502": {
            "$ref": "#/components/responses/AnthropicBadGateway"
          },
          "503": {
            "$ref": "#/components/responses/AnthropicServiceUnavailable"
          },
          "529": {
            "$ref": "#/components/responses/AnthropicProviderOverloaded"
          }
        }
      }
    },
    "/v1/responses": {
      "post": {
        "tags": [
          "Responses"
        ],
        "operationId": "createResponse",
        "summary": "Create a response (OpenAI Responses format)",
        "description": "OpenAI Responses-compatible endpoint, for clients such as the OpenAI\nAgents SDK and Codex: point the SDK's base URL at\n`https://api.router.africa/v1` and keep your code. Set `stream: true` to\nreceive Responses server-sent events.\n\nSupported parameters: `model`, `input`, `instructions`, `stream`,\n`temperature`, `max_output_tokens` and `tools` (function tools). A\n`max_output_tokens` above the model's ceiling is silently lowered to the\nceiling. Anything else is not part of the contract.\n\nErrors use the same envelope as `POST /v1/chat/completions`. An error\nafter streaming has started cannot change the `200`; it is sent as a\nfinal `data: {\"error\": {...}}` event and the stream ends.\n",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "router.africa extension. Works exactly as on `POST /v1/chat/completions`.\n",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResponseRequest"
              },
              "example": {
                "model": "openai/gpt-4.1-mini",
                "input": "Habari! Say hello in Swahili.",
                "max_output_tokens": 100
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The response, or a stream of events when `stream` is true.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              },
              "x-idempotent-replay": {
                "$ref": "#/components/headers/IdempotentReplay"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Response"
                },
                "example": {
                  "id": "resp_0192f0e0",
                  "object": "response",
                  "created_at": 1767225600,
                  "status": "completed",
                  "model": "openai/gpt-4.1-mini",
                  "output": [
                    {
                      "type": "message",
                      "id": "msg_0192f0e0",
                      "role": "assistant",
                      "status": "completed",
                      "content": [
                        {
                          "type": "output_text",
                          "text": "Habari!"
                        }
                      ]
                    }
                  ],
                  "usage": {
                    "input_tokens": 12,
                    "output_tokens": 3,
                    "total_tokens": 15
                  }
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Server-sent events in the Responses format: each event has an\n`event:` line naming its type and a `data:` line with one JSON\n`ResponseStreamEvent` (or, last, an error envelope). The stream\nends after `response.completed` (or `response.failed`), with no\n`[DONE]` line.\n"
                },
                "x-event-schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ResponseStreamEvent"
                    },
                    {
                      "$ref": "#/components/schemas/Error"
                    }
                  ]
                },
                "example": "event: response.created\ndata: {\"type\":\"response.created\",\"sequence_number\":0,\"response\":{\"id\":\"resp_0192f0e0\",\"object\":\"response\",\"created_at\":1767225600,\"status\":\"in_progress\",\"model\":\"openai/gpt-4.1-mini\",\"output\":[]}}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"sequence_number\":1,\"output_index\":0,\"item\":{\"type\":\"message\",\"id\":\"msg_0192f0e0\",\"role\":\"assistant\",\"status\":\"in_progress\",\"content\":[]}}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"sequence_number\":2,\"item_id\":\"msg_0192f0e0\",\"output_index\":0,\"content_index\":0,\"delta\":\"Habari!\"}\n\nevent: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"sequence_number\":3,\"item_id\":\"msg_0192f0e0\",\"output_index\":0,\"content_index\":0,\"text\":\"Habari!\"}\n\nevent: response.completed\ndata: {\"type\":\"response.completed\",\"sequence_number\":4,\"response\":{\"id\":\"resp_0192f0e0\",\"object\":\"response\",\"created_at\":1767225600,\"status\":\"completed\",\"model\":\"openai/gpt-4.1-mini\",\"output\":[{\"type\":\"message\",\"id\":\"msg_0192f0e0\",\"role\":\"assistant\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"text\":\"Habari!\"}]}],\"usage\":{\"input_tokens\":12,\"output_tokens\":3,\"total_tokens\":15}}}\n"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "408": {
            "$ref": "#/components/responses/RequestTimeout"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "529": {
            "$ref": "#/components/responses/ProviderOverloaded"
          }
        }
      }
    },
    "/v1/generation": {
      "get": {
        "tags": [
          "Generation"
        ],
        "operationId": "getGeneration",
        "summary": "Look up a past request",
        "description": "The metadata of one past request, by its request id (the `x-request-id`\nheader of that response). Metadata only: prompts and responses are never\nreturned, and there is no endpoint that returns them.\n\nOnly requests from the calling key's workspace can be read. Any other id,\nand an unknown id, return the same `404`.\n\nThe record is written after the response is sent, so a lookup straight\nafter a request can return `404` for a short while. Retry.\n\nAmounts are credits.\n",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "description": "The request id to look up.",
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The request's metadata.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Generation"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "anthropicApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Accepted on `POST /v1/messages` only, for Anthropic SDKs. Bearer also\nworks there. Never put a key in the URL or the body.\n"
      },
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API key (sk-ra-...)",
        "description": "Send your key as `Authorization: Bearer YOUR_KEY`. Never put a key in\nthe URL or the body: the request is refused with `key_not_in_header`.\n"
      }
    },
    "headers": {
      "RequestId": {
        "description": "Our id for this request. Quote it to support.",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "IdempotentReplay": {
        "description": "router.africa extension. Present, with the value `true`, only when this\nresponse is a stored replay of an earlier identical request (same body,\nor same `Idempotency-Key`) rather than a new run. A replay is not\ncharged again. Absent on a new run.\n",
        "required": false,
        "schema": {
          "type": "string",
          "enum": [
            "true"
          ]
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying. Always sent on `429`, `503` and `529`. When no better value is known the default is 5.",
        "required": true,
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      }
    },
    "schemas": {
      "Credits": {
        "type": "string",
        "pattern": "^[0-9]+\\.[0-9]{8}$",
        "description": "An amount of credits, as a decimal string with eight decimal places. Never a currency amount.",
        "examples": [
          "1250.00000000"
        ]
      },
      "ChatMessage": {
        "type": "object",
        "required": [
          "role"
        ],
        "properties": {
          "role": {
            "type": "string",
            "enum": [
              "system",
              "developer",
              "user",
              "assistant",
              "tool"
            ]
          },
          "content": {
            "description": "Text, or an array of content parts.",
            "type": [
              "string",
              "array",
              "null"
            ]
          }
        },
        "additionalProperties": true
      },
      "ChatCompletionRequest": {
        "type": "object",
        "required": [
          "model",
          "messages"
        ],
        "properties": {
          "model": {
            "type": "string",
            "description": "A model id from `GET /v1/models`, for example `openai/gpt-4.1-mini`."
          },
          "messages": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/ChatMessage"
            }
          },
          "stream": {
            "type": "boolean",
            "default": false
          },
          "max_tokens": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Output cap. Defaults to the model's default. A value above the model's ceiling is silently lowered to the ceiling; it is not an error."
          },
          "n": {
            "type": "integer",
            "const": 1
          },
          "temperature": {
            "type": "number"
          },
          "top_p": {
            "type": "number"
          },
          "stop": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ]
          },
          "presence_penalty": {
            "type": "number"
          },
          "frequency_penalty": {
            "type": "number"
          },
          "seed": {
            "type": "integer"
          },
          "response_format": {
            "type": "object"
          },
          "tools": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "tool_choice": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "object"
              }
            ]
          },
          "parallel_tool_calls": {
            "type": "boolean"
          },
          "logprobs": {
            "type": "boolean"
          },
          "top_logprobs": {
            "type": "integer"
          },
          "user": {
            "type": "string",
            "maxLength": 256
          }
        }
      },
      "Usage": {
        "type": "object",
        "required": [
          "prompt_tokens",
          "completion_tokens",
          "total_tokens"
        ],
        "properties": {
          "prompt_tokens": {
            "type": "integer"
          },
          "completion_tokens": {
            "type": "integer"
          },
          "total_tokens": {
            "type": "integer"
          }
        },
        "additionalProperties": true
      },
      "ChatCompletion": {
        "type": "object",
        "required": [
          "id",
          "object",
          "model",
          "choices"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "const": "chat.completion"
          },
          "created": {
            "type": "integer"
          },
          "model": {
            "type": "string"
          },
          "choices": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "index",
                "message",
                "finish_reason"
              ],
              "properties": {
                "index": {
                  "type": "integer"
                },
                "message": {
                  "$ref": "#/components/schemas/ChatMessage"
                },
                "finish_reason": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "additionalProperties": true
            }
          },
          "usage": {
            "$ref": "#/components/schemas/Usage"
          }
        },
        "additionalProperties": true
      },
      "ChatCompletionChunk": {
        "type": "object",
        "required": [
          "id",
          "object",
          "model",
          "choices"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "const": "chat.completion.chunk"
          },
          "created": {
            "type": "integer"
          },
          "model": {
            "type": "string"
          },
          "choices": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "index",
                "delta"
              ],
              "properties": {
                "index": {
                  "type": "integer"
                },
                "delta": {
                  "type": "object"
                },
                "finish_reason": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "additionalProperties": true
            }
          },
          "usage": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Usage"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "additionalProperties": true
      },
      "ModelList": {
        "type": "object",
        "required": [
          "object",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "object",
                "created",
                "owned_by"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "object": {
                  "type": "string",
                  "const": "model"
                },
                "created": {
                  "type": "integer"
                },
                "owned_by": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "KeyInfo": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "name",
              "limit",
              "limit_period",
              "usage",
              "limit_remaining",
              "expires_at",
              "balance"
            ],
            "properties": {
              "name": {
                "type": "string"
              },
              "limit": {
                "description": "The key's spend cap in credits, or null when it has none.",
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/Credits"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "limit_period": {
                "type": "string",
                "description": "How often the cap resets, or `none`."
              },
              "usage": {
                "description": "Credits used in the current cap period.",
                "$ref": "#/components/schemas/Credits"
              },
              "limit_remaining": {
                "description": "Credits left under the cap, or null when there is no cap.",
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/Credits"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "expires_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "balance": {
                "description": "The workspace's available credits (balance minus credits held for running requests).",
                "$ref": "#/components/schemas/Credits"
              }
            }
          }
        },
        "examples": [
          {
            "data": {
              "name": "production",
              "limit": "500.00000000",
              "limit_period": "monthly",
              "usage": "120.50000000",
              "limit_remaining": "379.50000000",
              "expires_at": null,
              "balance": "1250.00000000"
            }
          }
        ]
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "metadata"
            ],
            "properties": {
              "code": {
                "type": "integer",
                "description": "The HTTP status, as a number."
              },
              "message": {
                "type": "string",
                "description": "Safe to show to a user."
              },
              "metadata": {
                "type": "object",
                "required": [
                  "error_type",
                  "reason"
                ],
                "properties": {
                  "error_type": {
                    "type": "string",
                    "description": "The portable category. Branch on this for retry logic.",
                    "enum": [
                      "invalid_request",
                      "context_length_exceeded",
                      "authentication",
                      "payment_required",
                      "permission_denied",
                      "content_policy_violation",
                      "not_found",
                      "payload_too_large",
                      "rate_limit_exceeded",
                      "timeout",
                      "server",
                      "provider_unavailable",
                      "provider_overloaded"
                    ]
                  },
                  "reason": {
                    "type": "string",
                    "description": "The specific cause. Branch on this for user-facing handling.",
                    "enum": [
                      "invalid_body",
                      "key_not_in_header",
                      "context_length_exceeded",
                      "missing_api_key",
                      "invalid_api_key",
                      "insufficient_credits",
                      "key_spend_cap_exceeded",
                      "account_suspended",
                      "content_policy_violation",
                      "model_not_found",
                      "route_not_found",
                      "upstream_timeout",
                      "request_in_progress",
                      "payload_too_large",
                      "rate_limited",
                      "concurrency_limited",
                      "edge_rate_limited",
                      "internal_error",
                      "provider_error",
                      "service_unavailable",
                      "provider_overloaded"
                    ]
                  },
                  "model": {
                    "type": "string",
                    "description": "On `model_not_found`, the model id that was sent."
                  }
                }
              }
            }
          }
        }
      },
      "MessageContentBlock": {
        "type": "object",
        "required": [
          "type"
        ],
        "description": "A content block. `type` is for example `text`, `image`, `tool_use`,\n`tool_result` or `thinking`. `cache_control` may be set on a block.\n",
        "properties": {
          "type": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "cache_control": {
            "type": "object"
          }
        },
        "additionalProperties": true
      },
      "MessageRequest": {
        "type": "object",
        "required": [
          "model",
          "messages",
          "max_tokens"
        ],
        "properties": {
          "model": {
            "type": "string",
            "description": "A model id from `GET /v1/models`."
          },
          "messages": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "required": [
                "role",
                "content"
              ],
              "properties": {
                "role": {
                  "type": "string",
                  "enum": [
                    "user",
                    "assistant"
                  ]
                },
                "content": {
                  "description": "Text, or an array of content blocks.",
                  "oneOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MessageContentBlock"
                      }
                    }
                  ]
                }
              },
              "additionalProperties": true
            }
          },
          "max_tokens": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Output cap. A value above the model's ceiling is silently lowered to the ceiling; it is not an error."
          },
          "system": {
            "description": "System prompt, as text or an array of content blocks.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/MessageContentBlock"
                }
              }
            ]
          },
          "stream": {
            "type": "boolean",
            "default": false
          },
          "temperature": {
            "type": "number"
          },
          "top_p": {
            "type": "number"
          },
          "top_k": {
            "type": "integer"
          },
          "stop_sequences": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "tools": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "tool_choice": {
            "type": "object"
          },
          "thinking": {
            "type": "object",
            "description": "Extended thinking settings, as in Anthropic's API."
          },
          "context_management": {
            "type": "object"
          },
          "metadata": {
            "type": "object"
          }
        },
        "additionalProperties": true
      },
      "MessageUsage": {
        "type": "object",
        "required": [
          "input_tokens",
          "output_tokens"
        ],
        "properties": {
          "input_tokens": {
            "type": "integer"
          },
          "output_tokens": {
            "type": "integer"
          },
          "cache_creation_input_tokens": {
            "type": [
              "integer",
              "null"
            ]
          },
          "cache_read_input_tokens": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "additionalProperties": true
      },
      "Message": {
        "type": "object",
        "required": [
          "id",
          "type",
          "role",
          "model",
          "content",
          "stop_reason",
          "usage"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "const": "message"
          },
          "role": {
            "type": "string",
            "const": "assistant"
          },
          "model": {
            "type": "string"
          },
          "content": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MessageContentBlock"
            }
          },
          "stop_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "stop_sequence": {
            "type": [
              "string",
              "null"
            ]
          },
          "usage": {
            "$ref": "#/components/schemas/MessageUsage"
          }
        },
        "additionalProperties": true
      },
      "MessageStreamEvent": {
        "type": "object",
        "required": [
          "type"
        ],
        "description": "One streamed event. `type` is one of `message_start` (carries `message`),\n`content_block_start` (`index`, `content_block`), `content_block_delta`\n(`index`, `delta`), `content_block_stop` (`index`), `message_delta`\n(`delta` with `stop_reason`, and `usage`), `message_stop` or `ping`.\n",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "message_start",
              "content_block_start",
              "content_block_delta",
              "content_block_stop",
              "message_delta",
              "message_stop",
              "ping"
            ]
          },
          "index": {
            "type": "integer"
          },
          "message": {
            "$ref": "#/components/schemas/Message"
          },
          "content_block": {
            "$ref": "#/components/schemas/MessageContentBlock"
          },
          "delta": {
            "type": "object"
          },
          "usage": {
            "$ref": "#/components/schemas/MessageUsage"
          }
        },
        "additionalProperties": true
      },
      "AnthropicError": {
        "type": "object",
        "required": [
          "type",
          "error"
        ],
        "description": "Anthropic's error format, used by `POST /v1/messages`, plus our `reason`.",
        "properties": {
          "type": {
            "type": "string",
            "const": "error"
          },
          "error": {
            "type": "object",
            "required": [
              "type",
              "message",
              "reason"
            ],
            "properties": {
              "type": {
                "type": "string",
                "description": "Anthropic's error type, from the HTTP status.",
                "enum": [
                  "invalid_request_error",
                  "authentication_error",
                  "billing_error",
                  "permission_error",
                  "not_found_error",
                  "rate_limit_error",
                  "api_error",
                  "overloaded_error"
                ]
              },
              "message": {
                "type": "string",
                "description": "Safe to show to a user."
              },
              "reason": {
                "type": "string",
                "description": "Our specific cause, the same values as `metadata.reason` in the standard error.",
                "enum": [
                  "invalid_body",
                  "key_not_in_header",
                  "context_length_exceeded",
                  "missing_api_key",
                  "invalid_api_key",
                  "insufficient_credits",
                  "key_spend_cap_exceeded",
                  "account_suspended",
                  "content_policy_violation",
                  "model_not_found",
                  "route_not_found",
                  "upstream_timeout",
                  "request_in_progress",
                  "payload_too_large",
                  "rate_limited",
                  "concurrency_limited",
                  "edge_rate_limited",
                  "internal_error",
                  "provider_error",
                  "service_unavailable",
                  "provider_overloaded"
                ]
              },
              "model": {
                "type": "string",
                "description": "On `model_not_found`, the model id that was sent."
              }
            }
          }
        }
      },
      "ResponseInputItem": {
        "type": "object",
        "description": "An input item: a message with `role` and `content`, or a `function_call_output`.",
        "properties": {
          "type": {
            "type": "string"
          },
          "role": {
            "type": "string",
            "enum": [
              "system",
              "developer",
              "user",
              "assistant"
            ]
          },
          "content": {
            "description": "Text, or an array of content parts.",
            "type": [
              "string",
              "array"
            ]
          }
        },
        "additionalProperties": true
      },
      "ResponseRequest": {
        "type": "object",
        "required": [
          "model",
          "input"
        ],
        "properties": {
          "model": {
            "type": "string",
            "description": "A model id from `GET /v1/models`."
          },
          "input": {
            "description": "Text, or an array of input items.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ResponseInputItem"
                }
              }
            ]
          },
          "instructions": {
            "type": "string",
            "description": "A system instruction for the model."
          },
          "stream": {
            "type": "boolean",
            "default": false
          },
          "temperature": {
            "type": "number"
          },
          "max_output_tokens": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Output cap. Defaults to the model's default. A value above the model's ceiling is silently lowered to the ceiling; it is not an error."
          },
          "tools": {
            "type": "array",
            "description": "Function tools.",
            "items": {
              "type": "object",
              "required": [
                "type",
                "name"
              ],
              "properties": {
                "type": {
                  "type": "string",
                  "const": "function"
                },
                "name": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                },
                "parameters": {
                  "type": "object"
                },
                "strict": {
                  "type": [
                    "boolean",
                    "null"
                  ]
                }
              },
              "additionalProperties": true
            }
          }
        },
        "additionalProperties": true
      },
      "ResponseOutputItem": {
        "type": "object",
        "required": [
          "type"
        ],
        "description": "An output item: a `message` (with `content` parts of type `output_text`)\nor a `function_call` (`call_id`, `name`, `arguments`).\n",
        "properties": {
          "type": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "role": {
            "type": "string"
          },
          "content": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "additionalProperties": true
      },
      "ResponseUsage": {
        "type": "object",
        "required": [
          "input_tokens",
          "output_tokens",
          "total_tokens"
        ],
        "properties": {
          "input_tokens": {
            "type": "integer"
          },
          "output_tokens": {
            "type": "integer"
          },
          "total_tokens": {
            "type": "integer"
          }
        },
        "additionalProperties": true
      },
      "Response": {
        "type": "object",
        "required": [
          "id",
          "object",
          "created_at",
          "status",
          "model",
          "output"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "const": "response"
          },
          "created_at": {
            "type": "integer",
            "description": "Unix time, in seconds."
          },
          "status": {
            "type": "string",
            "enum": [
              "completed",
              "failed",
              "in_progress",
              "incomplete"
            ]
          },
          "model": {
            "type": "string"
          },
          "output": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResponseOutputItem"
            }
          },
          "usage": {
            "$ref": "#/components/schemas/ResponseUsage"
          },
          "instructions": {
            "type": [
              "string",
              "null"
            ]
          },
          "max_output_tokens": {
            "type": [
              "integer",
              "null"
            ]
          },
          "incomplete_details": {
            "type": [
              "object",
              "null"
            ]
          }
        },
        "additionalProperties": true
      },
      "ResponseStreamEvent": {
        "type": "object",
        "required": [
          "type"
        ],
        "description": "One streamed event. `type` is one of `response.created`,\n`response.in_progress` (both carry `response`), `response.output_item.added`\nand `response.output_item.done` (`output_index`, `item`),\n`response.content_part.added` and `response.content_part.done`\n(`item_id`, `output_index`, `content_index`, `part`),\n`response.output_text.delta` (`delta`), `response.output_text.done`\n(`text`), `response.function_call_arguments.delta` (`delta`) and\n`response.function_call_arguments.done` (`arguments`),\n`response.completed`, `response.failed` or `response.incomplete` (the\nfinal `response`, with `usage` on completion).\n",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "response.created",
              "response.in_progress",
              "response.output_item.added",
              "response.output_item.done",
              "response.content_part.added",
              "response.content_part.done",
              "response.output_text.delta",
              "response.output_text.done",
              "response.function_call_arguments.delta",
              "response.function_call_arguments.done",
              "response.completed",
              "response.failed",
              "response.incomplete"
            ]
          },
          "sequence_number": {
            "type": "integer"
          },
          "response": {
            "$ref": "#/components/schemas/Response"
          },
          "output_index": {
            "type": "integer"
          },
          "content_index": {
            "type": "integer"
          },
          "item_id": {
            "type": "string"
          },
          "item": {
            "$ref": "#/components/schemas/ResponseOutputItem"
          },
          "part": {
            "type": "object"
          },
          "delta": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "arguments": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "Generation": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "id",
              "created_at",
              "endpoint",
              "model",
              "status_code",
              "error_code",
              "latency_ms",
              "streamed",
              "input_tokens",
              "output_tokens",
              "cost"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "The request id."
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "endpoint": {
                "type": "string",
                "description": "The path that was called, for example `/v1/chat/completions`."
              },
              "model": {
                "type": "string"
              },
              "status_code": {
                "type": "integer",
                "description": "The HTTP status the request returned."
              },
              "error_code": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The error `reason`, or null when the request succeeded."
              },
              "latency_ms": {
                "type": "integer"
              },
              "streamed": {
                "type": "boolean"
              },
              "input_tokens": {
                "type": "integer"
              },
              "output_tokens": {
                "type": "integer"
              },
              "cost": {
                "description": "The charge, in credits.",
                "$ref": "#/components/schemas/Credits"
              }
            }
          }
        },
        "examples": [
          {
            "data": {
              "id": "0192f0e0-7a1c-7d3e-9f10-2b6c8a4d5e11",
              "created_at": "2026-11-07T09:30:00Z",
              "endpoint": "/v1/chat/completions",
              "model": "openai/gpt-4.1-mini",
              "status_code": 200,
              "error_code": null,
              "latency_ms": 840,
              "streamed": false,
              "input_tokens": 12,
              "output_tokens": 3,
              "cost": "0.00000240"
            }
          }
        ]
      }
    },
    "examples": {
      "InvalidBody": {
        "summary": "400 invalid_body",
        "value": {
          "error": {
            "code": 400,
            "message": "Request body is invalid: `messages` is required.",
            "metadata": {
              "error_type": "invalid_request",
              "reason": "invalid_body"
            }
          }
        }
      },
      "KeyNotInHeader": {
        "summary": "400 key_not_in_header",
        "value": {
          "error": {
            "code": 400,
            "message": "Send your API key in the Authorization header, not the URL or body.",
            "metadata": {
              "error_type": "invalid_request",
              "reason": "key_not_in_header"
            }
          }
        }
      },
      "ContextLengthExceeded": {
        "summary": "400 context_length_exceeded",
        "value": {
          "error": {
            "code": 400,
            "message": "The request is too long for `openai/gpt-4.1-mini`'s context window. Shorten the input and retry.",
            "metadata": {
              "error_type": "context_length_exceeded",
              "reason": "context_length_exceeded"
            }
          }
        }
      },
      "MissingApiKey": {
        "summary": "401 missing_api_key",
        "value": {
          "error": {
            "code": 401,
            "message": "You didn't provide an API key. Send it as `Authorization: Bearer YOUR_KEY`.",
            "metadata": {
              "error_type": "authentication",
              "reason": "missing_api_key"
            }
          }
        }
      },
      "InvalidApiKey": {
        "summary": "401 invalid_api_key",
        "value": {
          "error": {
            "code": 401,
            "message": "Incorrect API key provided: sk-ra-…abcd. Check your keys at https://app.router.africa.",
            "metadata": {
              "error_type": "authentication",
              "reason": "invalid_api_key"
            }
          }
        }
      },
      "InsufficientCredits": {
        "summary": "402 insufficient_credits",
        "value": {
          "error": {
            "code": 402,
            "message": "This workspace doesn't have enough credits for this request. Add credits at https://app.router.africa.",
            "metadata": {
              "error_type": "payment_required",
              "reason": "insufficient_credits"
            }
          }
        }
      },
      "KeySpendCapExceeded": {
        "summary": "402 key_spend_cap_exceeded",
        "value": {
          "error": {
            "code": 402,
            "message": "This API key has reached its spend limit. Raise the limit or use another key.",
            "metadata": {
              "error_type": "payment_required",
              "reason": "key_spend_cap_exceeded"
            }
          }
        }
      },
      "AccountSuspended": {
        "summary": "403 account_suspended",
        "value": {
          "error": {
            "code": 403,
            "message": "This account is suspended. Contact support.",
            "metadata": {
              "error_type": "permission_denied",
              "reason": "account_suspended"
            }
          }
        }
      },
      "ContentPolicyViolation": {
        "summary": "403 content_policy_violation",
        "value": {
          "error": {
            "code": 403,
            "message": "The request was blocked by the model provider's content policy.",
            "metadata": {
              "error_type": "content_policy_violation",
              "reason": "content_policy_violation"
            }
          }
        }
      },
      "ModelNotFound": {
        "summary": "404 model_not_found",
        "value": {
          "error": {
            "code": 404,
            "message": "The model `acme/unknown-model` does not exist or you do not have access to it.",
            "metadata": {
              "error_type": "not_found",
              "reason": "model_not_found",
              "model": "acme/unknown-model"
            }
          }
        }
      },
      "RouteNotFound": {
        "summary": "404 route_not_found",
        "value": {
          "error": {
            "code": 404,
            "message": "No such endpoint. Check the URL and method.",
            "metadata": {
              "error_type": "not_found",
              "reason": "route_not_found"
            }
          }
        }
      },
      "UpstreamTimeout": {
        "summary": "408 upstream_timeout",
        "value": {
          "error": {
            "code": 408,
            "message": "The model provider took too long to respond.",
            "metadata": {
              "error_type": "timeout",
              "reason": "upstream_timeout"
            }
          }
        }
      },
      "RequestInProgress": {
        "summary": "409 request_in_progress",
        "value": {
          "error": {
            "code": 409,
            "message": "An identical request is already in progress.",
            "metadata": {
              "error_type": "invalid_request",
              "reason": "request_in_progress"
            }
          }
        }
      },
      "PayloadTooLarge": {
        "summary": "413 payload_too_large",
        "value": {
          "error": {
            "code": 413,
            "message": "The request body is too large. Send less than 16 MiB.",
            "metadata": {
              "error_type": "payload_too_large",
              "reason": "payload_too_large"
            }
          }
        }
      },
      "RateLimited": {
        "summary": "429 rate_limited",
        "value": {
          "error": {
            "code": 429,
            "message": "Rate limit reached for this API key. Retry after 20 seconds.",
            "metadata": {
              "error_type": "rate_limit_exceeded",
              "reason": "rate_limited"
            }
          }
        }
      },
      "ConcurrencyLimited": {
        "summary": "429 concurrency_limited",
        "value": {
          "error": {
            "code": 429,
            "message": "Too many requests are running at once for this account. Wait for some to finish, then retry.",
            "metadata": {
              "error_type": "rate_limit_exceeded",
              "reason": "concurrency_limited"
            }
          }
        }
      },
      "EdgeRateLimited": {
        "summary": "429 edge_rate_limited",
        "value": {
          "error": {
            "code": 429,
            "message": "Too many requests from this address. Retry after 60 seconds.",
            "metadata": {
              "error_type": "rate_limit_exceeded",
              "reason": "edge_rate_limited"
            }
          }
        }
      },
      "InternalError": {
        "summary": "500 internal_error",
        "value": {
          "error": {
            "code": 500,
            "message": "Something went wrong on our side. Reference: 0192f0e0-7b1a-7c3e-9d4f-5a6b7c8d9e0f.",
            "metadata": {
              "error_type": "server",
              "reason": "internal_error"
            }
          }
        }
      },
      "ProviderError": {
        "summary": "502 provider_error",
        "value": {
          "error": {
            "code": 502,
            "message": "The model provider returned an error.",
            "metadata": {
              "error_type": "provider_unavailable",
              "reason": "provider_error"
            }
          }
        }
      },
      "ServiceUnavailable": {
        "summary": "503 service_unavailable",
        "value": {
          "error": {
            "code": 503,
            "message": "The service is temporarily unavailable.",
            "metadata": {
              "error_type": "server",
              "reason": "service_unavailable"
            }
          }
        }
      },
      "ProviderOverloaded": {
        "summary": "529 provider_overloaded",
        "value": {
          "error": {
            "code": 529,
            "message": "The model provider is overloaded. Retry shortly.",
            "metadata": {
              "error_type": "provider_overloaded",
              "reason": "provider_overloaded"
            }
          }
        }
      }
    },
    "responses": {
      "AnthropicBadRequest": {
        "description": "Same causes as the standard 400 response, in Anthropic's error format.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AnthropicError"
            },
            "example": {
              "type": "error",
              "error": {
                "type": "invalid_request_error",
                "message": "Request body is invalid: `messages` is required.",
                "reason": "invalid_body"
              }
            }
          }
        }
      },
      "AnthropicUnauthorized": {
        "description": "Same causes as the standard 401 response, in Anthropic's error format.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "WWW-Authenticate": {
            "description": "Always `Bearer realm=\"router.africa\"`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AnthropicError"
            },
            "example": {
              "type": "error",
              "error": {
                "type": "authentication_error",
                "message": "You didn't provide an API key. Send it as `x-api-key: YOUR_KEY` or `Authorization: Bearer YOUR_KEY`.",
                "reason": "missing_api_key"
              }
            }
          }
        }
      },
      "AnthropicPaymentRequired": {
        "description": "Same causes as the standard 402 response, in Anthropic's error format.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AnthropicError"
            },
            "example": {
              "type": "error",
              "error": {
                "type": "billing_error",
                "message": "This workspace doesn't have enough credits for this request. Add credits at https://app.router.africa.",
                "reason": "insufficient_credits"
              }
            }
          }
        }
      },
      "AnthropicForbidden": {
        "description": "Same causes as the standard 403 response, in Anthropic's error format.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AnthropicError"
            },
            "example": {
              "type": "error",
              "error": {
                "type": "permission_error",
                "message": "This account is suspended. Contact support.",
                "reason": "account_suspended"
              }
            }
          }
        }
      },
      "AnthropicNotFound": {
        "description": "Same causes as the standard 404 response, in Anthropic's error format.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AnthropicError"
            },
            "example": {
              "type": "error",
              "error": {
                "type": "not_found_error",
                "message": "The model `anthropic/unknown` does not exist or you do not have access to it.",
                "reason": "model_not_found"
              }
            }
          }
        }
      },
      "AnthropicRequestTimeout": {
        "description": "Same causes as the standard 408 response, in Anthropic's error format.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AnthropicError"
            },
            "example": {
              "type": "error",
              "error": {
                "type": "api_error",
                "message": "The model provider took too long to respond.",
                "reason": "upstream_timeout"
              }
            }
          }
        }
      },
      "AnthropicConflict": {
        "description": "Same causes as the standard 409 response, in Anthropic's error format.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AnthropicError"
            },
            "example": {
              "type": "error",
              "error": {
                "type": "invalid_request_error",
                "message": "An identical request is already in progress.",
                "reason": "request_in_progress"
              }
            }
          }
        }
      },
      "AnthropicPayloadTooLarge": {
        "description": "Same causes as the standard 413 response, in Anthropic's error format.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AnthropicError"
            },
            "example": {
              "type": "error",
              "error": {
                "type": "invalid_request_error",
                "message": "The request body is too large. Send less than 16 MiB.",
                "reason": "payload_too_large"
              }
            }
          }
        }
      },
      "AnthropicTooManyRequests": {
        "description": "Same causes as the standard 429 response, in Anthropic's error format.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AnthropicError"
            },
            "example": {
              "type": "error",
              "error": {
                "type": "rate_limit_error",
                "message": "Rate limit reached for this API key. Retry after 5 seconds.",
                "reason": "rate_limited"
              }
            }
          }
        }
      },
      "AnthropicInternalError": {
        "description": "Same causes as the standard 500 response, in Anthropic's error format.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AnthropicError"
            },
            "example": {
              "type": "error",
              "error": {
                "type": "api_error",
                "message": "Something went wrong on our side. Reference: 0192f0e0-aaaa-7000-8000-000000000000.",
                "reason": "internal_error"
              }
            }
          }
        }
      },
      "AnthropicBadGateway": {
        "description": "Same causes as the standard 502 response, in Anthropic's error format.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AnthropicError"
            },
            "example": {
              "type": "error",
              "error": {
                "type": "api_error",
                "message": "The model provider returned an error.",
                "reason": "provider_error"
              }
            }
          }
        }
      },
      "AnthropicServiceUnavailable": {
        "description": "Same causes as the standard 503 response, in Anthropic's error format.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AnthropicError"
            },
            "example": {
              "type": "error",
              "error": {
                "type": "api_error",
                "message": "The service is temporarily unavailable.",
                "reason": "service_unavailable"
              }
            }
          }
        }
      },
      "AnthropicProviderOverloaded": {
        "description": "Same causes as the standard 529 response, in Anthropic's error format.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AnthropicError"
            },
            "example": {
              "type": "error",
              "error": {
                "type": "overloaded_error",
                "message": "The model provider is overloaded. Retry shortly.",
                "reason": "provider_overloaded"
              }
            }
          }
        }
      },
      "BadRequest": {
        "description": "The request is malformed or fails validation.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "InvalidBody": {
                "$ref": "#/components/examples/InvalidBody"
              },
              "KeyNotInHeader": {
                "$ref": "#/components/examples/KeyNotInHeader"
              },
              "ContextLengthExceeded": {
                "$ref": "#/components/examples/ContextLengthExceeded"
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "The API key is missing or not valid. One answer for unknown, malformed, revoked, expired and pending keys.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "WWW-Authenticate": {
            "description": "Always `Bearer realm=\"router.africa\"`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "MissingApiKey": {
                "$ref": "#/components/examples/MissingApiKey"
              },
              "InvalidApiKey": {
                "$ref": "#/components/examples/InvalidApiKey"
              }
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "Not enough credits, or the key's spend cap is reached. Do not retry until the cause is fixed.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "InsufficientCredits": {
                "$ref": "#/components/examples/InsufficientCredits"
              },
              "KeySpendCapExceeded": {
                "$ref": "#/components/examples/KeySpendCapExceeded"
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "The account is suspended, or the provider refused the content.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "AccountSuspended": {
                "$ref": "#/components/examples/AccountSuspended"
              },
              "ContentPolicyViolation": {
                "$ref": "#/components/examples/ContentPolicyViolation"
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "The model is unknown, off or not allowed for this key (one answer for all three), or the path does not exist.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "ModelNotFound": {
                "$ref": "#/components/examples/ModelNotFound"
              },
              "RouteNotFound": {
                "$ref": "#/components/examples/RouteNotFound"
              }
            }
          }
        }
      },
      "RequestTimeout": {
        "description": "The model provider took too long, after retries and fallbacks.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "UpstreamTimeout": {
                "$ref": "#/components/examples/UpstreamTimeout"
              }
            }
          }
        }
      },
      "Conflict": {
        "description": "An identical request (same body, or same `Idempotency-Key`) is still running. Wait and retry; the first request's result is returned.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "RequestInProgress": {
                "$ref": "#/components/examples/RequestInProgress"
              }
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "The request body is over 16 MiB.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "PayloadTooLarge": {
                "$ref": "#/components/examples/PayloadTooLarge"
              }
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit reached. Back off and honour `Retry-After`. No `x-ratelimit-*` headers are sent.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "RateLimited": {
                "$ref": "#/components/examples/RateLimited"
              },
              "ConcurrencyLimited": {
                "$ref": "#/components/examples/ConcurrencyLimited"
              },
              "EdgeRateLimited": {
                "$ref": "#/components/examples/EdgeRateLimited"
              }
            }
          }
        }
      },
      "InternalError": {
        "description": "A fault on our side. Retry once, then contact support with the request id.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "InternalError": {
                "$ref": "#/components/examples/InternalError"
              }
            }
          }
        }
      },
      "BadGateway": {
        "description": "The model provider returned an error after fallbacks.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "ProviderError": {
                "$ref": "#/components/examples/ProviderError"
              }
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "Temporarily unavailable; refused before any model was called.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "ServiceUnavailable": {
                "$ref": "#/components/examples/ServiceUnavailable"
              }
            }
          }
        }
      },
      "ProviderOverloaded": {
        "description": "The model provider is overloaded and fallbacks are exhausted.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "ProviderOverloaded": {
                "$ref": "#/components/examples/ProviderOverloaded"
              }
            }
          }
        }
      }
    }
  }
}
