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.

ScopeGrants 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

MethodPathPurposeScope
POST/v1/chat/completionsChat completion (streaming and non-streaming)chat
POST/v1/completionsLegacy text completion (streaming and non-streaming)completions
POST/v1/embeddingsCreate embeddingsembeddings

Models

MethodPathPurposeScope
GET/v1/modelsList public models available to the keymodels
GET/v1/models/{id}Get one public model by slugmodels

Account

MethodPathPurposeScope
GET/v1/balanceToken quota and prepaid credit balancebalance
GET/v1/subscriptionsSubscription tier, plan, and statusbalance
GET/v1/packagesRead-only platform package catalogbalance
GET/v1/account/analytics?period_days=Self-service usage/latency/cost summarybalance

Health (public, no key)

MethodPathPurpose
GET/healthLiveness — process is up (200)
GET/v1/healthSame as /health
GET/statusSame as /health
GET/health/liveKubernetes-style liveness probe
GET/health/readyReadiness — can serve chat/embeddings now (200/503)
GET/health/providersPer-provider health breakdown (200/503)
GET/health/detailedCapabilities, providers, and license diagnostics

Metrics (operator)

MethodPathPurposeAccess
GET/metrics/Aggregate service metrics (JSON)admin scope
GET/metrics/providersPer-provider metrics (JSON)admin scope
GET/metrics/providers/{provider}One provider's metrics (JSON)admin scope
GET/metrics/costCost estimates (JSON)admin scope
GET/metrics/prometheusPrometheus text expositionToken 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

FieldTypeNotes
modelstringRequired. Public model slug.
messagesarrayRequired, non-empty. Must include at least one user message.
streambooleanDefault false. true returns SSE.
temperaturenumber02, default 1.
max_tokensintegerOptional, > 0.
top_pnumber01, default 1.
ninteger≥ 1, default 1.
stopstring or arrayOptional.
presence_penaltynumber-22, default 0.
frequency_penaltynumber-22, default 0.
toolsarrayOptional. Function/tool definitions.
tool_choicestring or objectOptional. Requires a non-empty tools.
response_formatobjectOptional.
seed, logit_bias, userOptional, 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

FieldTypeNotes
modelstringRequired. Public model slug.
inputstring or arrayRequired, non-empty.
encoding_formatstringOptional: float or base64.
userstringOptional.

POST /v1/completions (legacy)

FieldTypeNotes
modelstringRequired.
promptstring or arrayA single string, or a single-element array. Multi-element prompt arrays are not supported.
plus the sampling fieldsmax_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

StatustypecodeWhen it happens
400invalid_request_errorvalidation_errorBody failed schema validation (bad field, empty messages, no user turn, etc.).
400invalid_request_errormalformed_jsonRequest body is not valid JSON.
400invalid_request_errorinvalid_function_call_payloadMalformed tools / tool_choice.
401authentication_errormissing_api_keyNo key in the request.
401authentication_errorinvalid_api_keyKey not recognized.
401authentication_errorrevoked_api_keyKey was revoked — do not retry, do not re-auth.
401authentication_errorinactive_api_keyKey is inactive — an admin can reactivate it.
401authentication_errorexpired_api_keyKey passed its expiry.
401authentication_erroraccount_not_activeThe owning account is not active.
402insufficient_creditsinsufficient_creditsAccount is out of prepaid credit — top up, don't retry.
403permission_errorscope_deniedKey lacks the endpoint's scope.
403permission_errormetrics_forbiddenPrometheus scrape without a valid token / allowed CIDR.
404model_not_foundmodel_not_foundModel slug is unknown or not available to the key.
413invalid_request_errorpayload_too_largeRequest body exceeds GATEWAY_MAX_BODY_BYTES (default 10 MiB).
429rate_limit_errorquota_exceededMonthly token quota is exhausted.
429rate_limit_errorrate_limit_exceededPer-key requests-per-minute limit exceeded (includes Retry-After).
500api_errorinternal_errorUnexpected server error.
503service_unavailableauth_service_unavailableTransient auth-infrastructure failure (includes Retry-After) — retry.

How to react

  • Retryable: 429 (after Retry-After), 503. Back off and retry.
  • Not retryable as-is: 400, 401, 402, 403, 404 — fix the request, key, credit, scope, or model first.
  • 402 specifically: 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