Skip to main content

Running inference through Anchorage’s Agentic Banking Platform

POST /v1/chat/completions speaks the same request shape as OpenAI’s own chat completions API. Point any OpenAI-compatible SDK’s base_url at it and it works as a drop-in swap, including streaming with "stream": true (see Streaming). POST /v1/messages speaks Anthropic’s Messages API on the same host, so Claude Code and an Anthropic SDK’s message-creation calls work against it (see Anthropic Messages API).

Why run inference here

  • Nothing to set up. No account with the provider, no invoice, no separate billing relationship. Anchorage handles payment on the agent’s behalf and sends the request straight to the provider — point base_url at the endpoint and go.
  • Settles instantly. No waiting on an external biller; the call completes as soon as it’s paid.
  • Anchorage carries the payment infrastructure. Your organization never has to hold, manage, or expose any payment credentials of its own to use this.
  • The same budget. The budget that pays for other purchases on this platform pays for inference too, drawn from the agent’s identity here rather than a separate balance held with the provider.
  • The same governance controls. The org’s budget cap, the per-agent daily cap, and instant revocation all apply to inference spend exactly as they do to any other agent payment, approved and reconciled the same way. A budget with the default allow-all merchant setting needs no extra configuration; one restricted to a specific allowlist needs the inference provider added to it (see prerequisites).
  • Drop-in for existing code. Point an existing OpenAI-compatible integration at this endpoint and swap the API key. The request and response shapes don’t change.

How a call is billed

The agent’s API key authenticates the call (see Agents; no OAuth session works here). Anchorage bills the charge to the agent’s primary budget, gated by the same compliance and spending checks as any other agent payment. A charge over the budget cap waits for an org admin to approve it instead of completing; retry with the same request once it’s approved (see Approvals). A non-streamed completion priced by metered usage is declined instead when its per-request maximum exceeds the remaining budget, so the budget must cover that maximum. Once payment is made, a completion or an ordinary provider rejection (its own rate limit, for example) passes through unchanged: status, Content-Type, body, and a Retry-After header when the provider sent one. Either way the response carries X-Transaction-ID. A response the provider can’t relay this way falls back to a specific error instead: one too large to record in full is response_truncated_after_payment; anything else — no completion coming back at all, or one in a shape this endpoint can’t safely relay — is upstream_failed_after_payment (see Failure modes below).

Streaming

Set "stream": true to receive the completion as the provider produces it. Each chunk is relayed as the provider sends it, so an OpenAI-compatible SDK’s streaming mode reads it unchanged. A streamed completion always pays the merchant’s fixed-price option, so a charge over the budget cap waits for an org admin to approve it. A non-streamed completion can pay a metered offer instead, which is declined when its per-request maximum exceeds the remaining budget. The charge is submitted before the first chunk arrives, and X-Transaction-ID is in the response headers. A streamed response can run for at most 120 seconds. A stream still going at that point is cut off the same way as one that passes the size limit below, and the charge stays as submitted, so cap the output length of requests that could take longer. The charge for the full completion is submitted when the stream starts, and it stays pending until the payment settles on chain (see Settlement). A stream that ends early doesn’t reduce the charge. If the connection closes before the provider’s final event arrives, treat what you received as incomplete. If the provider rejects the paid request, you get its status and body unchanged, the same as for a non-streamed call. A streamed completion can be at most 10 MiB. A stream that passes that size is cut off: the connection closes without a normal end of stream, the same as any other stream that ends early, and the charge stays as submitted. A provider rejection whose body passes that size returns upstream_failed_after_payment instead of the provider’s own status and body. Sending the same streamed request again with the same idempotency key doesn’t charge you again, and it doesn’t replay the earlier stream. The retry reuses the first request’s payment authorization, which has already been used, so the provider is likely to reject it and the retry is likely to fail. To get a new completion, send the request with a new Idempotency-Key.

Managing idempotency keys

To avoid multiple charges on retries, every request carries an idempotency key. If you don’t send an Idempotency-Key header, we generate one from your agent and the request body, so sending the exact same request twice only charges you once. Send Idempotency-Key yourself to control this directly. Reuse the same key on a second request and you don’t pay again, even if you changed the body: the key guarantees you’re not charged twice, not that you get the exact same response back. Usually you get the first request’s result again; on rare occasions the original response wasn’t retained, and the provider is contacted again for a fresh one. Send a new key when you actually want to send a new request with the same body. The response normally tells you which one you got. A response that reused an earlier payment says so, with the time the original request was first submitted, and a response whose body is the first request’s recorded result is also marked as a replay. A chat completion that reused the payment without the replay mark was normally fetched from the provider again for this response; either way, you weren’t charged again. A streamed response is always relayed fresh, so it is never marked as a replay. The marks are a hint, not proof; see Idempotency. Your idempotency key is scoped to your agent, so two agents on the same account can each reuse the same key value without colliding. Use something unique per request you actually want to send, like a UUID.

Failure modes

Anthropic Messages API

POST /v1/messages takes the request body of Anthropic’s Messages API and passes it to the provider unchanged, including cache_control blocks and any other field. The provider’s status, Content-Type, and body come back unchanged. The provider’s Retry-After and Retry-After-Ms headers also come back on a provider error and on any response that isn’t streamed. Other provider response headers aren’t relayed. With "stream": true, the route relays Anthropic’s server-sent events as the provider sends them (see Streaming). Message creation is the one Messages API operation served: an SDK’s token-counting call isn’t. Set model to a Claude model’s identifier from the model list. The route authenticates with the agent’s API key, sent either as the x-api-key header, as Anthropic’s API expects, or as a bearer token. If a request sends both, they must carry the same key, and a request may carry only one of each. The anthropic-version and anthropic-beta headers are forwarded to the provider; other request headers aren’t. Every request sends exactly one anthropic-version value, as Anthropic’s API requires: a request that omits it, repeats it, or lists more than one version is refused with a 400 invalid_request_error, and nothing is charged. A message is billed and approved exactly like a chat completion: from the agent’s primary budget, with the same X-Transaction-ID header and the same 1.5 MiB request limit (see How a call is billed). Repeats follow the same idempotency-key rules (see Managing idempotency keys), with one addition: when you don’t send an Idempotency-Key, the generated key covers the forwarded anthropic-version and anthropic-beta headers as well as the body, so changing a beta header makes a new request. When a message waits for approval, retry it with the same body and the same forwarded headers. A message and a chat completion never share a payment, even when their bodies are identical. Errors on this route use Anthropic’s error shape, so an Anthropic SDK raises its usual exception types:
A failure listed under Failure modes keeps its status on this route, and error.type follows from that status the way Anthropic’s API assigns it: invalid_request_error (400), authentication_error (401), billing_error (402), permission_error (403), request_too_large (413), and api_error for a 5xx. A 422 is invalid_request_error. A request that uses a method other than POST gets a 405 invalid_request_error with an Allow: POST header. This route carries no error.code, so errors that share a status share a type; error.message says which failure it is and what to do. A 4xx the provider returns passes through in its own Anthropic shape. A provider 5xx before payment is a 502 api_error, and nothing is charged.

Image and video generation

POST /v1/images/generations and POST /v1/videos/generations take a generation request on the same host, with the same agent API key, and bill it the same way as a chat completion: from the agent’s primary budget, behind the same compliance and spending checks, with an over-cap charge waiting for an org admin’s approval (see How a call is billed). A model that finishes within the request answers 200 with the result, the same way a chat completion does. A generation that takes longer answers 202 with a job: its id, its status, and a poll_url on the inference host. poll_url is a path, /v1/images/generations/{id} or /v1/videos/generations/{id}, to request with GET on https://inference.agentic.anchorage.com. Only the agent that submitted a job can read it.

Polling drives the job

The provider works on the generation on its own, and Anchorage learns its progress only when you poll, so the job’s status moves forward only while you poll it. A job has about an hour from its submit to finish: a job still unfinished after that ends as timed_out, however often it was polled. A completed job answers every later poll from its record. The poll answers with the job’s id, status, and price_atomic (its price in the payment’s smallest unit: the submit-time quote until a poll observes a price, then the most recent poll’s price, and on a completed job the price the provider reported for it). status is queued, in_progress, completed, failed, or timed_out. completed means the provider reported the generation finished and paid for; the job’s transaction, described below, is the record of the charge itself. A completed job carries data, the provider’s own array of generated output, such as image or video URLs, when there is output to return. A completed job has no data when the provider sent none, or when the output was larger than about 2.5 million characters and too large to record. A job that ended any other way carries failure_reason.

What a generation costs

A generation is one payment, however many times you poll it. The submit signs one payment authorization and reserves its price against the budget, and no poll signs another. The provider can charge that authorization at any point after the submit. The job’s id is also its payment’s transaction id, and that transaction’s status is the record of whether you were charged. The payment’s page at https://agentic.anchorage.com/payments/{id} shows it. If the provider never charges it, the reserved amount is released back to the budget once the authorization is confirmed unused, which can take until shortly after the authorization expires.

How a job ends without completing

A job’s failure_reason says why the job ended, not whether the payment was charged. Whatever the ending, check the job’s transaction before you pay for the generation again. A charged transaction means the generation was paid for; a pending one can still be charged, or be released once its authorization expires. For the three endings Anchorage concludes, a poll within a day of the submit returns the job as completed once the provider reports it finished, with its output when one was recorded. Keep polling such a job during that day to collect it.

Repeating a submit

A submit carries an idempotency key the same way a chat completion does (see Managing idempotency keys). Sending a submit again under the same key, or the same body with no key, reuses the first submit’s payment instead of paying for a second generation. Usually it attaches to the job the first submit started and answers with that job’s id and poll_url; on rare occasions the provider answers the repeated submit differently, and you get its answer instead. When the repeat answers with a job, the response reports the reused payment and isn’t marked as a replay, because it reports the job’s current status. A submit that sent no key and one that sent a key never share a payment, so a retry keeps the first submit’s choice. Use a new Idempotency-Key for a new generation.

Generation errors

A submit returns the same errors as a chat completion (see Failure modes), plus these: A generation submit can also return 403 payment_refused when the provider offers its lowest price only on a rail that can’t present one authorization on every poll. Nothing is signed, the message names the rail, and the same request gets the same answer until the provider’s prices change. A poll returns:

Audio generation

Three routes on the same host generate audio from text: POST /v1/audio/speech reads input aloud, POST /v1/audio/sound-effects makes the sound effect text describes, and POST /v1/audio/generations makes the music track prompt describes. Each is billed, repeated, and answered like an image or video generation, with the same errors (see Image and video generation): one payment per generation from the agent’s primary budget, behind the same compliance and spending checks. A callable audio model is served by exactly one of the three routes, the one matching the unit it’s priced in: speech models per 1K characters, sound effects per generation, and music per track. The model list’s endpoint names that route. Each route answers either 200 with the audio, when the generation finishes within the request, or 202 with a job. Speech and sound effects usually finish within the request, and music usually answers with a job, but any route can give either answer. A job’s poll_url is its submit path followed by its id, such as /v1/audio/generations/{id}, and it’s polled exactly like an image or video job (see Polling drives the job). A 200 is the provider’s answer passed through unchanged, and a completed job carries the provider’s data array when it returned one. The provider defines these entries, and Anchorage doesn’t reshape them. So far, speech answers and minimax/music-2.5+ jobs have carried a url in each entry, pointing at the provider’s storage, and a music entry has also carried the track’s duration_seconds. Those links expire, so download the audio when the answer arrives.

Typed judgments

POST /v1/decide answers questions about a piece of text your code can’t read reliably on its own: yes/no, pick one of several options, or a score. It’s free. It doesn’t draw on a budget or create a payment, and the same agent API key authenticates it. A request carries a state (the text, or any JSON value, to judge) and up to 20 named questions. Each question sets a type, its instructions, and, for choice and score, its criteria. model is optional and defaults to openjev. The response carries one answer per question name, under answers. Anchorage passes the provider’s request and response shapes through unchanged. The provider may retain the text you send under its standard terms, so don’t send data you can’t share with a third party.