API reference
Endpoints, authentication, scopes, request and response shapes, and the complete error table for the Cordy Gateway data plane.
This is the reference for the Cordy Gateway data-plane API — the OpenAI-compatible surface your clients call on port 8002. For runnable examples see Client integration.
Base URL and versioning
https://<host>:8002/v1
Every inference and account endpoint is available both under the /v1 prefix and as a non-/v1 alias (for example, /v1/chat/completions and /chat/completions are the same handler). Prefer the /v1 form for OpenAI-SDK compatibility. The one exception is usage analytics, which is only available at /v1/account/analytics.
Authentication
Send a wnx_ API key in one of two headers:
Authorization: Bearer wnx_<key>
X-API-Key: wnx_<key>
Keys must be sent in a header. Keys in the URL query string are not accepted. The gateway stores only the SHA-256 hash of each key.
Scopes
Each endpoint family requires a scope. An API key carries a list of granted scopes; a request to an endpoint whose scope the key does not hold returns 403 scope_denied.
| Scope | Grants access to |
|---|---|
chat | /v1/chat/completions |
completions | /v1/completions |
embeddings | /v1/embeddings |
models | /v1/models, /v1/models/{id} |
balance | /v1/balance, /v1/subscriptions, /v1/packages, /v1/account/analytics |
admin | /metrics/* (operator metrics) |
An empty scope list is treated as legacy default-allow: the key can reach any endpoint its owner is otherwise permitted to use. A populated list is an allow-list. Issue new keys with the least privilege they need — for example a chat-only key for a chat feature.
Endpoints
Inference
| Method | Path | Purpose | Scope |
|---|---|---|---|
| POST | /v1/chat/completions | Chat completion (streaming and non-streaming) | chat |
| POST | /v1/completions | Legacy text completion (streaming and non-streaming) | completions |
| POST | /v1/embeddings | Create embeddings | embeddings |
Models
| Method | Path | Purpose | Scope |
|---|---|---|---|
| GET | /v1/models | List public models available to the key | models |
| GET | /v1/models/{id} | Get one public model by slug | models |
Account
| Method | Path | Purpose | Scope |
|---|---|---|---|
| GET | /v1/balance | Token quota and prepaid credit balance | balance |
| GET | /v1/subscriptions | Subscription tier, plan, and status | balance |
| GET | /v1/packages | Read-only platform package catalog | balance |
| GET | /v1/account/analytics?period_days= | Self-service usage/latency/cost summary | balance |
Health (public, no key)
| Method | Path | Purpose |
|---|---|---|
| GET | /health | Liveness — process is up (200) |
| GET | /v1/health | Same as /health |
| GET | /status | Same as /health |
| GET | /health/live | Kubernetes-style liveness probe |
| GET | /health/ready | Readiness — can serve chat/embeddings now (200/503) |
| GET | /health/providers | Per-provider health breakdown (200/503) |
| GET | /health/detailed | Capabilities, providers, and license diagnostics |
Metrics (operator)
| Method | Path | Purpose | Access |
|---|---|---|---|
| GET | /metrics/ | Aggregate service metrics (JSON) | admin scope |
| GET | /metrics/providers | Per-provider metrics (JSON) | admin scope |
| GET | /metrics/providers/{provider} | One provider's metrics (JSON) | admin scope |
| GET | /metrics/cost | Cost estimates (JSON) | admin scope |
| GET | /metrics/prometheus | Prometheus text exposition | Token and/or CIDR gated |
/metrics/prometheus is not part of the API-key surface. In production it is denied unless the caller presents the GATEWAY_METRICS_TOKEN bearer token or originates from a CIDR in GATEWAY_METRICS_ALLOWED_CIDRS. See Deployment.
Request and response shapes
The inference endpoints follow the OpenAI schemas. The notable request fields:
POST /v1/chat/completions
| Field | Type | Notes |
|---|---|---|
model | string | Required. Public model slug. |
messages | array | Required, non-empty. Must include at least one user message. |
stream | boolean | Default false. true returns SSE. |
temperature | number | 0–2, default 1. |
max_tokens | integer | Optional, > 0. |
top_p | number | 0–1, default 1. |
n | integer | ≥ 1, default 1. |
stop | string or array | Optional. |
presence_penalty | number | -2–2, default 0. |
frequency_penalty | number | -2–2, default 0. |
tools | array | Optional. Function/tool definitions. |
tool_choice | string or object | Optional. Requires a non-empty tools. |
response_format | object | Optional. |
seed, logit_bias, user | — | Optional, OpenAI-standard. |
A non-streaming response is a standard chat.completion object with choices, usage, and observability headers (X-Channel-Id, X-Provider, X-Failover-Count, X-Routing-Overhead-Ms). A streaming response is text/event-stream of chat.completion.chunk events terminated by [DONE].
Message content must be text — a string, or an array of {"type":"text","text":"..."} parts. Non-text parts are rejected. See Client integration.
POST /v1/embeddings
| Field | Type | Notes |
|---|---|---|
model | string | Required. Public model slug. |
input | string or array | Required, non-empty. |
encoding_format | string | Optional: float or base64. |
user | string | Optional. |
POST /v1/completions (legacy)
| Field | Type | Notes |
|---|---|---|
model | string | Required. |
prompt | string or array | A single string, or a single-element array. Multi-element prompt arrays are not supported. |
| plus the sampling fields | — | max_tokens, temperature, top_p, n, stream, stop, penalties, user. |
Error format
All errors use the OpenAI-style envelope:
{
"error": {
"type": "authentication_error",
"message": "API key has been revoked.",
"code": "revoked_api_key",
"request_id": "…"
}
}
code is a stable string you can branch on; type groups related errors; request_id correlates with server logs. Some errors add fields (for example required_scope and granted_scopes on scope_denied, or details on validation errors).
Error table
| Status | type | code | When it happens |
|---|---|---|---|
| 400 | invalid_request_error | validation_error | Body failed schema validation (bad field, empty messages, no user turn, etc.). |
| 400 | invalid_request_error | malformed_json | Request body is not valid JSON. |
| 400 | invalid_request_error | invalid_function_call_payload | Malformed tools / tool_choice. |
| 401 | authentication_error | missing_api_key | No key in the request. |
| 401 | authentication_error | invalid_api_key | Key not recognized. |
| 401 | authentication_error | revoked_api_key | Key was revoked — do not retry, do not re-auth. |
| 401 | authentication_error | inactive_api_key | Key is inactive — an admin can reactivate it. |
| 401 | authentication_error | expired_api_key | Key passed its expiry. |
| 401 | authentication_error | account_not_active | The owning account is not active. |
| 402 | insufficient_credits | insufficient_credits | Account is out of prepaid credit — top up, don't retry. |
| 403 | permission_error | scope_denied | Key lacks the endpoint's scope. |
| 403 | permission_error | metrics_forbidden | Prometheus scrape without a valid token / allowed CIDR. |
| 404 | model_not_found | model_not_found | Model slug is unknown or not available to the key. |
| 413 | invalid_request_error | payload_too_large | Request body exceeds GATEWAY_MAX_BODY_BYTES (default 10 MiB). |
| 429 | rate_limit_error | quota_exceeded | Monthly token quota is exhausted. |
| 429 | rate_limit_error | rate_limit_exceeded | Per-key requests-per-minute limit exceeded (includes Retry-After). |
| 500 | api_error | internal_error | Unexpected server error. |
| 503 | service_unavailable | auth_service_unavailable | Transient auth-infrastructure failure (includes Retry-After) — retry. |
How to react
- Retryable:
429(afterRetry-After),503. Back off and retry. - Not retryable as-is:
400,401,402,403,404— fix the request, key, credit, scope, or model first. 402specifically: stop and top up credit; retrying will not help.
Streaming errors
In a streaming response, an upstream failure after the stream has opened is delivered as an in-band SSE error event (with a string code), followed by [DONE]. A pre-stream failure (routing, budget, connect) is returned as a normal JSON error with the appropriate status code and never opens an SSE stream. The gateway also emits a stream_interrupted event if the upstream closes without a finish reason.
Next steps
- Client integration — SDK usage and headers.
- Configuration — the environment variables referenced here.