Skip to content

Responses

POST https://api.katara.com/v1/responses takes the OpenAI Responses request shape and is served by Katara Cortex. Routing, payment and charges follow the same rules as Chat Completions; a request with a key is paid with that key, one without a key per request with x402. The OpenAI SDKs and the Vercel AI SDK work against it with https://api.katara.com/v1 as the base URL.

Terminal window
curl 'https://api.katara.com/v1/responses' \
-H "Authorization: Bearer $KATARA_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"model": "katara/cortex",
"input": "Explain a Merkle tree in two sentences.",
"max_output_tokens": 200
}'

Read assistant text from output items whose type is message, then from their content parts whose type is output_text. A reply can also hold function calls, reasoning items and web search calls.

Any model is accepted, including the GPT names OpenAI clients send by default, and Katara Cortex serves it (Model names in requests). The reply’s model is katara/cortex. A request paid with x402 may name katara/cortex-genius or katara/cortex-smart to fix the model; its reply still names katara/cortex, and a key that names either is refused.

input is a string or an array of typed items. Message roles are user, assistant, system and developer. A message’s content is text or typed parts: input_text, output_text and refusal; input_image by image_url or file_id; input_file by file_id or file_data; and input_audio. Files are uploaded through /v1/files. instructions is sent as a system message.

Function tools use a flat declaration:

{
"type": "function",
"name": "read_file",
"parameters": {
"type": "object",
"properties": { "path": { "type": "string" } },
"required": ["path"]
}
}

A returned function_call carries call_id, name and arguments as JSON text. Run the tool in your application, then send the call and its result in the next request, or send only the result with previous_response_id:

[
{"type":"message","role":"user","content":"Read README.txt"},
{"type":"function_call","call_id":"call_1","name":"read_file","arguments":"{\"path\":\"README.txt\"}"},
{"type":"function_call_output","call_id":"call_1","output":"Project setup instructions"}
]

Each result must match an outstanding call. A function_call_output can be a string or a list of parts, images and files included.

Besides function, Katara takes custom tools (freeform input, with a grammar described to the model), namespace tools (one level deep), tool_search and web_search. tool_choice takes auto, none, required, a named function or custom tool, and allowed_tools; parallel_tool_calls: false gets at most one call per reply. Hosted tools Katara does not run are refused with 400 unsupported_tool naming the tool: file_search, mcp, computer, computer_use and computer_use_preview, code_interpreter, image_generation, local_shell, shell and apply_patch.

A web_search tool (web_search_preview and the dated variants too) lets the model search the web through Katara. max_tool_calls lowers the cap of five searches per request, and filters.allowed_domains or filters.blocked_domains narrows the results. Each search comes back as a web_search_call item with its sources, and the answer’s output_text carries url_citation annotations for the pages it quotes. In a stream the item opens with response.web_search_call.in_progress and response.web_search_call.searching when the search starts, and ends with response.web_search_call.completed once its results are in. Once the model asks to search, the rest of the request is served by Katara’s managed models only. If it then ends without an answer, the searches and the model calls after the first are not charged. Search needs a key; Web search has the price and when it is offered.

reasoning (any effort, summary) is accepted and changes nothing: Katara Cortex decides how much the model reasons. Reasoning comes back as reasoning items, with summary_text when you asked for a summary. Reasoning items can be sent back in history. include values are accepted; Katara returns no encrypted reasoning, and encrypted_content sent back is dropped.

text.format takes text, json_object or json_schema, and text.verbosity is accepted. background: true is accepted and answered when the reply is complete. truncation, metadata, user, safety_identifier, service_tier, stream_options, top_logprobs, prompt_cache_retention, context_management, client_metadata and access_programs are accepted. prompt_cache_key keeps a conversation on the model whose cache holds its start (Caching). A stored prompt template is refused. Any other field is refused with 400 unsupported_parameter naming it.

Set stream: true for server-sent events. Each event has a name, JSON data and a sequence_number. The stream starts with response.created and response.in_progress. Text arrives in response.output_text.delta, tool arguments in response.function_call_arguments.delta and response.custom_tool_call_input.delta, and reasoning in response.reasoning_text.delta or the summary events.

Wait for response.completed, response.incomplete or response.failed; a closed connection is not enough to establish completion. An incomplete response names max_output_tokens or content_filter in incomplete_details.reason. A failed stream’s error.code is one of the codes in Errors.

The final response carries usage (input_tokens, with cached_tokens and cache_write_tokens under input_tokens_details; output_tokens, with reasoning_tokens; total_tokens) and the katara object with the charge. See Usage and cost.

A response is stored unless the request sends store: false, and is kept 30 days from when it was created, then deleted. Reading or continuing it does not extend that. Stored responses are kept in their own store, apart from ordinary logs.

  • With a key, a response belongs to the key’s account, and only keys of that account can read, continue or delete it.
  • Without a key, a response belongs to nobody: no account or payer address is kept with it, and whoever holds its id can read, continue or delete it. Treat the id as a secret.

The routes:

  • previous_response_id continues from a stored response: its input and output become the start of the new request.
  • conversation (an id, or {"id": ...}) continues from the latest stored response in that conversation. It needs a key.
  • GET /v1/responses/{response_id} returns a stored response. ?stream=true is refused.
  • GET /v1/responses/{response_id}/input_items lists its input items, newest first, or oldest first with order=asc.
  • DELETE /v1/responses/{response_id} deletes it and answers {"object": "response.deleted"}.
  • POST /v1/responses/{response_id}/cancel answers 400 response_not_cancellable: every response has finished by the time it is answered.

A response that is missing, expired, deleted or someone else’s is 404 response_not_found; as previous_response_id it is 404 previous_response_not_found. If saving a response fails, the reply is still delivered; only the continuation is lost.

Format errors are 400 with param naming the field: invalid_json, invalid_type, invalid_value, unsupported_parameter, unsupported_tool, and invalid_request for a malformed or oversized body. Payment and capacity errors are those of Chat Completions, passed through unchanged: 402 insufficient_quota when the funds cannot cover the request, 429 provider_rate_limited, 500 when every candidate’s reply failed or was refused, and 503 with Retry-After.