# Nexfer Inference > Nexfer Inference is an OpenAI-compatible inference gateway at https://nexference.com. > Get a working key in one call, point any OpenAI client at /v1, and pay per token in USD. BASE URL https://nexference.com/v1 (OpenAI-compatible data plane) https://nexference.com/api (control plane: accounts, keys, usage) GET A KEY IN ONE CALL (no browser needed) POST https://nexference.com/api/agent/register Content-Type: application/json {"name":"my-agent"} // all fields optional // optional: "email":"you@example.com", "models":["", ...] 201 Created { "account": { "id": "usr_...", "email": "agent+...@nexfer.invalid", "name": "my-agent", "role": "user", "status": "active", "verified": false, "balance_usd": 0.5, "spent_month_usd": 0, "requests_month": 0, "monthly_cap_usd": 1, "rpm": 20, "created_at": "2026-10-27T08:15:18.593Z", "last_login_at": null }, "key": { "id": "key_...", "name": "my-agent", "prefix": "nxf_live_OVeJ357u", "status": "active", "scopes": ["chat","models","usage"], "models": null, "rpm": 20, "concurrency": 2, "monthly_cap_usd": 1, "daily_cap_usd": 0.5, "expires_at": null, "allowed_ips": null, "created_at": "2026-10-27T08:15:18.746Z", "last_used_at": null, "grace_until": null, "revoked_at": null, "spend_month_usd": 0, "spend_today_usd": 0, "requests_month": 0, "upstream": "pending" }, "secret": "nxf_live_OVeJ357u_<32-hex-secret>", "base_url": "https://nexference.com/v1", "models_url": "https://nexference.com/v1/models", "docs_url": "https://nexference.com/docs.md", "llms_txt": "https://nexference.com/llms.txt", "note": "Store the secret now: it is shown once. Rotate with POST /api/keys/:id/rotate." } The `secret` is shown once and is the only credential you need. If you register without an email, a placeholder address is created for you; nothing is emailed. AUTHORIZATION Every data-plane request carries the key as a bearer token: Authorization: Bearer nxf_live__ Keys are prefixed `nxf_live_`. The prefix is safe to display; the secret is not. OPENAI-COMPATIBLE ENDPOINTS (data plane) GET /v1/models list models (public, no auth required; cached 60s) POST /v1/chat/completions chat completions (streaming and non-streaming) POST /v1/completions legacy text completions POST /v1/embeddings embeddings passthrough POST /v1/responses responses API passthrough POST /v1/systemone decisions models (see DECISIONS MODELS; the exact path is each decisions model's `endpoint` field) GET /v1/key this key's metadata + limits (needs "models" scope) GET /v1/usage?days=30 this key's usage summary (needs "usage" scope) Request and response bodies are the standard OpenAI bodies. Point any OpenAI SDK at base_url https://nexference.com/v1 and use the secret as api_key. This gateway is not an open forwarder: only the paths above — the static OpenAI surface plus every `endpoint` the catalogue publishes — are forwarded. A request to any other /v1/... path returns 404 `not_found`. Sending a decisions model to a chat path (or a chat model to a decisions endpoint) returns 400 `invalid_request` with a hint naming the correct path. Response headers added by the gateway on inference calls: x-nexfer-request-id request id (also echoed in usage records) x-nexfer-key the key prefix x-nexfer-balance-usd account balance after the call x-nexfer-cost-usd charged cost of this call, USD (non-stream, and last chunk on stream) MODEL DISCOVERY GET https://nexference.com/v1/models 200 OK { "object": "list", "data": [ { "id": "gpt-oss-20b", // send this verbatim as `model` "object": "model", "name": "GPT-OSS 20B", "kind": "chat", // "chat" or "decisions" "owned_by": "nexfer", "context_length": 131072, "free": true, // free models cost nothing to call "free_until": "2026-11-04", // if set: free until this date (else null/absent) "uncensored": false, "created": 0, "pricing": { "prompt_usd_per_m": 0, // retail USD per 1,000,000 prompt tokens "completion_usd_per_m": 0, // retail USD per 1,000,000 completion tokens "cache_read_usd_per_m": 0, "long_context": [ // optional; tiered long-context rates { "above_tokens": 100000, "prompt_usd_per_m": 0.5625, "completion_usd_per_m": 2.875, "cache_read_usd_per_m": 0.05625 } ] } } ]} Use the `id` field verbatim as the `model` in your requests. `pricing` is the retail price you pay (markup already included). `free: true` models cost 0, and `free_until` (when present) marks a model that is free until that date. Models of `kind: "decisions"` also carry an `endpoint` (the path to call them on). DECISIONS MODELS Models with `kind: "decisions"` classify or score the `state` you give them and are NOT called at /v1/chat/completions — call them on the `endpoint` the catalogue publishes for the model (currently /v1/systemone). Send a `model`, the `state` (the text or JSON the questions are about) and a map of named `questions`. Question types: {"type":"score", "min":0, "max":10} // a numeric score {"type":"choice", "options":["formal","casual"], // one of the labels "criteria":{"formal":"…","casual":"…"}} // optional per-label criteria {"type":"noul"} // yes/no (probability) POST https://nexference.com/v1/systemone { "model": "clef", "state": "The team shipped the feature on time and every test passed.", "questions": { "quality": { "type": "score", "min": 0, "max": 10 }, "tone": { "type": "choice", "options": ["formal","casual"], "criteria": { "formal": "uses formal language", "casual": "uses casual language" } }, "flag": { "type": "noul" } } } 200 OK { "answers": { "quality": { "type": "score", "score": 3.7239, "confidence": 0.5694, "probabilities": { "0": 0.0099, "4": 0.7909 }, "legend": { "0": "very unlikely", "4": "very likely" } }, "tone": { "type": "choice", "choice": "formal", "confidence": 0.7419, "probabilities": { "formal": 0.9307, "casual": 0.0693 } }, "flag": { "type": "noul", "noul": 0.9415 } }, "id": "dec-0f0977b3030caf45b8b378e0", "model": "clef", "object": "decision", "usage": { "prompt_tokens": 308, "completion_tokens": 0, "total_tokens": 308 } } Each answer's `type` matches its question. Billing for decisions is input tokens only, and `usage` is normalised to OpenAI names (prompt_tokens / completion_tokens / total_tokens). STREAMING Send `"stream": true` for server-sent events (OpenAI SSE format, ending in `data: [DONE]`). If you omit `stream_options`, the gateway adds `stream_options.include_usage = true` automatically so usage can be metered; the final events carry a `usage` object. A trailing SSE comment `: nexfer usage prompt_tokens=… completion_tokens=…` carries the meter and is ignored by standard OpenAI clients. PRICING AND MARKUP Prices are quoted per million tokens (USD) in each model's `pricing`. They are final retail prices: a markup is already applied, and there are no separate platform fees. `free: true` models are free (`free_until` marks a model that is free until a date). If a model carries `pricing.long_context` tiers, the tier with the highest `above_tokens` that your request's prompt exceeds replaces the base rates for that request, and metering follows the same rule. Every call debits your account balance; `x-nexfer-cost-usd` and the usage object report what was charged. Balance may go slightly negative after a call; the next call is refused with `insufficient_balance` until you top up. LIMITS ENFORCED PER REQUEST (in order) - key syntax, existence, status (active / rotating / revoked), expiry, IP allowlist - key scopes (chat / models / usage) and the key's model allowlist - account balance must be > 0 - account and per-key monthly spend caps, then per-key daily spend cap - per-key requests-per-minute (rpm) and concurrency ceiling Defaults: rpm 60, concurrency 4; per-key monthly cap $10 and daily cap $5; new agent keys: rpm 20, concurrency 2, monthly cap $1, daily cap $0.50. Request body cap 2 MiB; `max_tokens` / `max_completion_tokens` are clamped to the model's own output limit and 32768, whichever is lower. Rejections are recorded and returned with a Nexfer error code. ERROR CODES JSON error shape (data plane follows the OpenAI shape): { "error": { "message": "...", "type": "...", "code": "...", "param": null } } code HTTP meaning invalid_request 400 malformed body, missing/invalid field, or a model sent to the wrong endpoint (chat vs decisions) unauthorized 401 missing or invalid API key key_revoked 401 the key was revoked key_expired 401 the key passed its expires_at key_grace_expired 401 a rotated key's grace window has closed forbidden 403 not permitted for this key/account scope_missing 403 key lacks the required scope model_not_allowed 403 key's allowlist excludes this model ip_not_allowed 403 request from a disallowed address account_paused 403 account is paused insufficient_balance 402 balance exhausted; top up to continue monthly_cap 402 monthly spend cap reached (key or account) daily_cap 402 per-key daily spend cap reached rate_limited 429 requests-per-minute limit reached concurrency_limited 429 too many in-flight requests on this key not_found 404 unknown endpoint (any /v1/... path not published here), unknown key or model conflict 409 illegal state change (e.g. deleting a live key) upstream_error 502 the model produced an error upstream_unavailable 503 inference temporarily unavailable timeout 504 the model timed out internal 500 unexpected server error KEY ROTATION AND REVOCATION (control plane, session or cookie auth) POST /api/keys/:id/rotate {"grace_minutes":1440} -> 200 { key, secret, previous: { id, prefix, grace_until } } Same key id, new secret. The old prefix keeps working until `grace_until` (default 1440 minutes), then returns 401 key_grace_expired. POST /api/keys/:id/revoke {"reason":"..."} -> 200 { key } status becomes "revoked"; the key stops working immediately. DELETE /api/keys/:id -> 200 { ok: true } permanent delete of an already-revoked key. KEY INTROSPECTION GET /v1/key -> this key's id, prefix, status, scopes, models, caps and the account balance/status. Needs the "models" scope. QUICKSTART (works verbatim — set your key first) export NEXFER_API_KEY=nxf_live_... # the `secret` from registration curl https://nexference.com/v1/models -H "Authorization: Bearer $NEXFER_API_KEY" curl https://nexference.com/v1/chat/completions -H "Authorization: Bearer $NEXFER_API_KEY" -H 'Content-Type: application/json' -d '{"model":"MODEL_ID","messages":[{"role":"user","content":"ping"}]}' The first call lists model ids; use one of those ids as MODEL_ID. On a non-production deployment, replace the host with the `base_url` returned by your registration response. MORE Full documentation: https://nexference.com/docs.md (human-readable) OpenAPI 3.1: https://nexference.com/openapi.json Status: https://nexference.com/healthz