API reference

Change the base URL. Keep your code.

Tīrtha speaks three dialects: OpenAI Chat, Anthropic Messages, and OpenAI Responses. Plain text, tools, and streaming work on all three. Below are copy-paste examples, the full config reference, and the error codes.

Quickstart

One base URL, one key.

Any OpenAI-compatible client works. Point it at the Tīrtha base URL, use your key, and set the model to tirtha/verified. The gateway chooses the tier for you.

# curl curl https://api.tirtha.ai/v1/chat/completions \ -H "Authorization: Bearer sk-tirtha-…" \ -H "Content-Type: application/json" \ -d '{ "model": "tirtha/verified", "messages": [{"role":"user","content":"Write a function that reverses a string."}] }'
# python, the openai sdk from openai import OpenAI client = OpenAI(base_url="https://api.tirtha.ai/v1", api_key="sk-tirtha-…") r = client.chat.completions.create( model="tirtha/verified", messages=[{"role":"user","content":"Write a function that reverses a string."}], ) print(r.choices[0].message.content)
Chat completions

The primary path.

POST /v1/chat/completions. Standard OpenAI request and response shapes. System prompts and multi-turn history are passed through. The response is the OpenAI shape you already parse.

# -> 200 { "id": "chatcmpl-…", "object": "chat.completion", "choices": [{ "index": 0, "message": {"role":"assistant","content":"def reverse(s): return s[::-1]"}, "finish_reason": "stop" }], "usage": {"prompt_tokens":18,"completion_tokens":9,"total_tokens":27} }

Note: temperature is fixed at 0 and model is chosen by the gateway, so both are ignored if you send them. See the config reference below for the full honored-versus-ignored list.

Streaming

Server-sent events, usage on the last chunk.

Set stream: true. To get token usage in the stream, set stream_options: {"include_usage": true} and read it from the final chunk.

curl https://api.tirtha.ai/v1/chat/completions \ -H "Authorization: Bearer sk-tirtha-…" \ -d '{ "model":"tirtha/verified", "stream":true, "stream_options":{"include_usage":true}, "messages":[{"role":"user","content":"…"}] }' # SSE frames data: {"choices":[{"delta":{"content":"def "}}]} data: {"choices":[{"delta":{"content":"reverse"}}]} data: {"choices":[{"delta":{}}],"usage":{"total_tokens":27}} data: [DONE]
Tool calling

OpenAI wire format, checked before it is served.

Send tools and, if you want, tool_choice. Tool calls come back in the OpenAI tool_calls shape, identical across all three dialects. Every tool call passes an internal check before it reaches you.

{ "model": "tirtha/verified", "messages": [{"role":"user","content":"What is the weather in Paris tomorrow?"}], "tools": [{"type":"function","function":{ "name":"get_forecast", "parameters":{"type":"object","properties":{"city":{"type":"string"},"day":{"type":"string"}}} }}] } # -> assistant turn "tool_calls": [{"id":"call_1","type":"function", "function":{"name":"get_forecast","arguments":"{\"city\":\"Paris\",\"day\":\"tomorrow\"}"}}]
Dialects

Same engine, three front doors.

Point Claude Code at Messages, point Codex at Responses, point everything else at Chat. Tools and streaming work on all three.

DialectEndpointFor
OpenAI ChatPOST /v1/chat/completionsThe default; most SDKs and tools
Anthropic MessagesPOST /v1/messagesClaude Code and Anthropic SDKs; system and history are native
OpenAI ResponsesPOST /v1/responsesCodex and the Responses SDK

The reply is translated back into the dialect you called, so your existing client parses it unchanged. Usage fields and stop reasons follow each dialect's own convention.

Verify

Ask if an answer is backed by its source.

POST /v1/verify. Not part of the OpenAI standard, so point at it directly. Give a source and a claim; get a verdict, a confidence, and a short reason. It is a consistency check against the source, not a fact-checker of the world.

curl https://api.tirtha.ai/v1/verify \ -H "Authorization: Bearer $TIRTHA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "source": "The trial reduced symptoms in 6 of 10 patients.", "claim": "The drug is effective." }' # The key is required. Without it the edge returns 403 before the request # reaches us. This example previously omitted the header, so copying it failed. # Corrected 2026-08-06 against the live endpoint. # # STRICT contract, anything else is a 400 naming the offending field: # source the grounding text # claim the assertion to check # action optional, one of: serve | cache | memory | deploy | publish (default serve) # query optional, the retrieval query # retrieved optional, what retrieval returned # test_report optional, an execution result to fold in # -> 200 { "decision": "allow", # or "refuse" "confidence": 0.82, "reasoning": "6 of 10 responders supports a claim of effectiveness.", "usage": {"tokens_in":31,"tokens_out":14} }

Public, no key required, typically one to two seconds. Try it in the browser with no setup at tirtha.ai/verify.

Config reference

Every param, honored or dropped.

Standard OpenAI parameters

ParamHonoredDefaultNotes
messagesYesrequiredSystem prompt and full history passed through
tools, tool_choiceYesnoneTriggers the tool-calling path
streamYesfalseServer-sent events
stream_optionspartialnoneOnly include_usage is read
response_formatYesnone{"type":"json_object"} passed through
max_tokensYestier capCapped to the serving tier's limit
temperatureignored0Fixed at 0; determinism makes reuse safe
modelignoredtirtha/verifiedThe gateway selects the tier
top_pignored-Declared for spec; moot at temperature 0
stopsoon-Declared, not yet wired

Tīrtha extensions  (body.tirtha.*)

FieldTypeDefaultPurpose
modestringautoCare dial: auto · max · value · low. Request overrides key overrides account.
testsstringnoneThe most useful field here. Checks that must pass before an answer is trusted; failures escalate. Also the only way to get a cached answer: we re-run your tests against a previously verified solution and return it only if they pass. Without tests we never serve from cache, and every request generates fresh.
no_cachebooleanfalseSkip the cache for this request, both directions: nothing is read from it and nothing is written to it. Use it when the prompt carries something you do not want stored. Read this with the privacy note: when an answer passes verification we store it together with the prompt that produced it, in a store that is shared by default, so no_cache is the per-request opt out. Verified live on 2026-08-06: without the flag a repeated request comes back served_by: cache-exact; with it, both calls go to the model.
domainstringkey defaultScope tag for reuse of solved work
subdomainstringkey defaultSub-scope tag
Response & the refuse contract

Which tier answered, and whether it was checked.

Every response carries a tirtha object beside the standard OpenAI fields, so you always know which tier served the answer and whether it passed the gate.

FieldMeaning
served_byWhich tier answered: a lower-cost model (e.g. deepinfra), frontier-value / frontier-max, cache-exact, or cache-crosscontext (a solution first written in another context, re-verified against your tests before serving).
cache_eligiblefalse when you sent no tests, which means this request could not be served from cache. The response also carries cache_hint explaining it. Send tests to make a request cache-eligible.
verifiedtrue only if the answer passed the gate — your tests for code, or structural + schema validation for a tool call. false = best-effort (a plain-text answer, or a declined call).
escalatedtrue if a frontier model was used. The cascade climbs lower-cost → frontier-value → frontier-max only as far as the gate requires.
modeThe care dial in effect for this request (auto/low/value/max).

The refuse contract (tool-calling)

The core reliability promise: Tīrtha never serves a tool call it cannot validate. When the model is not confident a tool applies, it does not invent a call and does not return an error. It returns a normal 200 with an empty tool_calls array and plain text explaining the limit, with verified:false. A malformed or schema-invalid lower-cost call is escalated to a frontier model first; only if no valid call can be produced is the empty-call answer returned.

# Trap: only a CURRENT-weather tool exists; asked for a 3-day forecast { "choices": [{ "message": { "content": "I can only retrieve the current weather. I can't forecast future dates.", "tool_calls": [] # refused — no invented call }}], "tirtha": { "served_by":"deepinfra", "verified":false, "escalated":false } }
Errors

What comes back when it will not serve.

Errors follow the OpenAI error shape. Two behaviors are worth knowing: rate and quota return 429, and hitting your daily spend cap does not fail. It degrades to the lower cost path and tells you so in the response.

StatusTypeMeaning
401invalid_api_keyMissing or bad Bearer key
429rate_limit_errorPer-key rate exceeded. Honor Retry-After.
429quota_exceededMonthly quota exceeded
429server_busyConcurrency gate full; retry shortly
200meta.spend_capped:trueDaily spend cap reached; served on the lower cost path, never a silent switch
200meta.premium_limited:trueIncluded premium allowance reached; served on the lower cost path
{ "error": { "type":"rate_limit_error", "message":"Rate limit exceeded. Retry after 60s." } }
More

The rest of the story.

The server info card has the capabilities matrix, limits, cache policy, and the honesty block. Security has the data posture. Pricing has the tiers. Found a wrong answer? Report it and we send a receipt.