Documentation

Endpoint Reference

POST /v1/ai/complete

The gateway's one AI completion endpoint. Requires the x-api-key header (see Authentication).

Request

{
  "prompt": "Say hello in exactly three words.",
  "max_tokens": 4096,
  "effort": "low"
}
FieldTypeRequiredNotes
promptstringyesMust be non-empty (after trimming whitespace).
max_tokensintegernoThe most output tokens the reply may use, 1–8192. Default 4096. The model's own reasoning counts toward it, so leave room for long answers. You're billed for the tokens actually used, not this cap.
effortstringnoHow much the model reasons before answering: "none", "low", "medium" or "high". Lower is faster and uses fewer output tokens, so it costs less; higher is more thorough. "none" turns the reasoning step off. Leave it out to get the gateway's default (normally the provider's own, which equals "high"). Any other value is a 400.
servicestringnoWhich AI service runs the request, by id (see GET /v1/ai/services below). Leave it out to use your default service. It must be one of the services enabled for your account, spelled exactly; anything else is a 400 that lists the allowed ids.
fallbackboolnofalse forbids sending this request to a different service if yours is unavailable, e.g. for data that must stay with one vendor. Leave it out to follow your account's setting, which is off unless your operator has switched it on for you (see GET /v1/ai/services).

Reasoning is billed as output tokens, so effort changes what a request costs, and max_tokens caps reasoning and reply together. For short or routine requests, "low" usually returns noticeably faster; keep the default (or "high") where answer quality matters most.

Response — 200 OK

{
  "response": "Hello there, friend!",
  "truncated": false,
  "tenant_name": "Vaultwave Financial",
  "used_byok_key": false,
  "cost": "0.00044",
  "balance_after": "2459.99934",
  "effort": "low",
  "service": "anthropic-claude-sonnet-5",
  "usage": {
    "input_tokens": 14,
    "output_tokens": 6,
    "thinking_tokens": 0
  }
}
FieldTypeNotes
responsestringThe completion text.
truncatedbooltrue if the reply hit max_tokens and was cut off: its end is missing. Retry with a larger max_tokens or a shorter request.
tenant_namestringThe name of the tenant that authenticated this request.
used_byok_keybooltrue if your own provider key was used instead of the platform's — see Billing.
costdecimal stringWhat this request was billed. Always "0" when used_byok_key is true.
balance_afterdecimal stringYour balance immediately after this request. For a BYOK request, this is simply your current balance — unaffected by the request.
effortstringOnly present when an effort level was applied: the one you sent, or the gateway's default.
servicestringThe id of the service that handled the request: the one you named, or your default, or (see fallback_from) a fallback.
fallback_fromstringOnly present when your requested service was unavailable and a fallback service answered instead: the service you asked for.
usageobjectThe tokens this request used: input_tokens, output_tokens (everything generated, reasoning included; this is what cost is computed from) and, when the provider reports it, thinking_tokens (the reasoning part of output_tokens). Rarely, estimated: true: the provider reported no usage, so these counts (and cost) are a conservative estimate; only services set up to allow that do this.

New fields may be added to this response over time; ignore any you don't use.

Errors

Every error from this gateway — on every route, not just this one — is a JSON body shaped {"error": "<message>"}, paired with an HTTP status. The cases below are the ones you can actually hit calling this endpoint, with real example bodies captured from a live gateway (not invented):

StatusCauseExample body
400 Bad RequestEmpty or whitespace-only prompt{"error":"prompt must not be empty"}
400 Bad Requestmax_tokens outside 1–8192{"error":"max_tokens must be between 1 and 8192"}
400 Bad Requesteffort is not one of the allowed values{"error":"effort must be one of: none, low, medium, high"}
400 Bad Requestservice is not one of your enabled services{"error":"service must be one of: anthropic-claude-sonnet-5"}
400 Bad RequestThe service can't be used right now (no provider key configured for it){"error":"service anthropic-claude-sonnet-5 is not available"}
401 UnauthorizedMissing or unrecognized x-api-key{"error":"Missing or invalid API key"}
402 Payment RequiredYour balance can't cover even a minimal request (metered billing only — never returned for a BYOK-configured tenant){"error":"Insufficient balance"}
403 ForbiddenYour tenant has been suspended by an operator{"error":"This tenant's access has been suspended"}
429 Too Many RequestsYou've exceeded your rate limit — see Rate Limits{"error":"Rate limit exceeded — slow down and try again shortly"}
502 Bad GatewayThe gateway couldn't reach (or got an unexpected response from) the upstream AI provider — not something you did wrong{"error":"Upstream AI provider request failed"} or {"error":"Upstream AI provider returned an unexpected response"}
503 Service UnavailableThe AI provider already has as many requests in flight as the gateway allows (in total, or for your account); retry shortly{"error":"Upstream AI provider is busy — try again shortly"}
503 Service UnavailableThe provider has failed repeatedly, so the gateway is pausing requests to it for a short while; retry shortly{"error":"Upstream AI provider is temporarily unavailable — try again shortly"}

A couple of things worth knowing about how these are checked, in order: rate limit first (before anything else runs, so a rate-limited request never touches the database or your balance), then the request's fields (prompt, max_tokens, effort, service), then — for metered (non-BYOK) tenants only — the balance check, before the AI provider is ever called at all. A BYOK-configured tenant skips the balance check entirely, since the platform isn't fronting the cost of that request.

GET /v1/ai/services

The AI services your account may use, with what each supports and costs. Requires the x-api-key header and counts toward your rate limit, like /v1/ai/complete.

Response — 200 OK

{
  "default_service": "anthropic-claude-sonnet-5",
  "services": [
    {
      "id": "anthropic-claude-sonnet-5",
      "display_name": "Claude Sonnet 5",
      "provider": "Anthropic",
      "default": true,
      "available": true,
      "billing": "metered",
      "capabilities": {
        "effort": ["none", "low", "medium", "high"],
        "max_output_tokens": 8192
      },
      "price": {
        "currency": "KES",
        "input_per_1000_tokens": "1.0000",
        "output_per_1000_tokens": "5.0000"
      }
    }
  ],
  "fallback": {"enabled": false, "chain": []}
}
FieldTypeNotes
default_servicestringThe service used when a request has no service field.
services[].idstringWhat to send as service. Ids are permanent.
services[].availableboolfalse when the service can't be used right now.
services[].billingstring"metered": charged to your balance at price. "own_key": your own provider key is used and nothing is charged.
services[].capabilities.effortarrayThe effort values this service applies. An empty list means it ignores effort (the response then has no effort field).
services[].capabilities.max_output_tokensintegerThe highest max_tokens this service accepts.
fallbackobjectWhether requests to an unavailable service may be sent to another of your services instead, and which, in order. Off unless your operator switched it on; a fallback is only ever used when the first service is unavailable (never because it refused your request), and the response says so in fallback_from.
services[].priceobjectPer 1,000 tokens, in currency (KES unless that service is priced otherwise). For a price in another currency, kes_equivalent gives the same price in KES at today's exchange rate.

Your balance, cost and balance_after are always in KES. A service priced in another currency is converted to KES at the exchange rate in effect when your request starts, and the charge is rounded to 6 decimal places. If that service can't be priced right now (for example, no current exchange rate), it shows "available": false and requests to it get a 400 saying it is not available; nothing is charged.

Errors: 401, 403 and 429 as for /v1/ai/complete.

Funding your balance

There is no API endpoint for deposits: you top up from your dashboard (Deposits), and the gateway credits your balance automatically once the payment settles.