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_urlat 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 anIdempotency-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:
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 astimed_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’sid 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’sid 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.