# ACTAVA — Cura API documentation (full text) > Machine-readable mirror of the live Cura API docs at https://www.actava.ai/cura/docs. > Cura 1T (API id actava/cura-soar) is ACTAVA's healthcare language model, > served through an OpenAI-compatible API at https://inference.actava.ai/v1. # Quickstart Source: https://www.actava.ai/cura/docs ## Get an API key API keys are issued by ACTAVA. [Join the waitlist](https://forms.gle/hLfVUq1pBPRNojkj8) and we'll set you up. Once you have a key, export it: `Shell` ``` export ACTAVA_API_KEY="your-key" ``` ## Base URL and auth All requests go to the base URL below with a bearer token. Two endpoints are available: `/v1/models` and `/v1/chat/completions`. `HTTP` ``` https://inference.actava.ai/v1 Authorization: Bearer $ACTAVA_API_KEY ``` ## Your first request ``` curl https://inference.actava.ai/v1/chat/completions \ -H "Authorization: Bearer $ACTAVA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "actava/cura-soar", "messages": [{"role": "user", "content": "Summarize the escalation criteria for chest pain triage."}], "temperature": 1.0 }' ``` ## Response Standard chat-completion shape. `usage.prompt_tokens_details.cached_tokens` reports how much of your prompt was served from cache. `JSON` ``` { "id": "chatcmpl-...", "object": "chat.completion", "model": "actava/cura-soar", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Escalate immediately when chest pain presents with..." }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 21, "completion_tokens": 214, "total_tokens": 235, "prompt_tokens_details": { "cached_tokens": 0 } } } ``` ## Streaming Set `"stream": true` to receive server-sent events as the model generates. See the [API reference](https://www.actava.ai/cura/docs/api) for the chunk format. ## Next steps - [Models](https://www.actava.ai/cura/docs/models) — what `/v1/models` returns - [API reference](https://www.actava.ai/cura/docs/api) — every parameter, streaming, tools, vision, errors - [Prompt caching](https://www.actava.ai/cura/docs/guides/caching) — automatic prefix caching for repeated prompts # Introduction Source: https://www.actava.ai/cura/docs/introduction ## The model Cura 1T (API id `actava/cura-soar`) is ACTAVA's healthcare-specialized model — a one-trillion-parameter fine-tune of Kimi-K2.6 trained through recursive self-improvement. It handles patient communication, clinical reasoning over text and images, and agentic EHR workflows. It accepts text and images and generates text, with a 256K context window. ## Endpoints | Endpoint | Purpose | | --- | --- | | GET /v1/models | List the models your key can access | | POST /v1/chat/completions | Generate responses — including streaming, vision, and tool calls | The API follows the OpenAI request/response format, so OpenAI SDKs work unchanged — see [OpenAI compatibility](https://www.actava.ai/cura/docs/openai-compat). ## Authentication Every request carries `Authorization: Bearer $ACTAVA_API_KEY`. Keys are issued by ACTAVA — [join the waitlist](https://forms.gle/hLfVUq1pBPRNojkj8) for access. Keep keys in environment variables or a secret manager; never commit them. ## Tokens Usage is metered per token: input and output are counted separately, and every response reports exact counts in `usage` — including how many prompt tokens were served from cache. Repeated prompt prefixes are cached automatically — [Prompt caching](https://www.actava.ai/cura/docs/guides/caching) explains how to benefit from it. ## Sampling Cura 1T is evaluated at `temperature 1.0` — the setting all published benchmark numbers use. We recommend it as your default; lower it only if your application requires highly deterministic phrasing. ## Research preview Cura 1T is a research model. Build with clinician oversight for anything patient-facing, and design workflows so a human validates clinical decisions. Rate limits are set per key at issue time — contact us if you need more throughput. # Model list Source: https://www.actava.ai/cura/docs/models ## GET /v1/models `Shell` ``` curl https://inference.actava.ai/v1/models \ -H "Authorization: Bearer $ACTAVA_API_KEY" ``` `200 OK (abridged)` ``` { "data": [ { "id": "actava/cura-soar", "name": "AVA: Cura Soar 1.0", "input_modalities": ["text", "image"], "output_modalities": ["text"], "context_length": 262144, "max_output_length": 32768, "supported_features": ["tools", "reasoning"], "is_ready": true } ] } ``` Entries are richer than the bare OpenAI shape — they carry modalities, context and output limits, and supported features. Read fields defensively rather than assuming the OpenAI `object`/`owned_by` keys. ## Available models | Model id | Name | Modalities | Context window | | --- | --- | --- | --- | | actava/cura-soar | Cura 1T | Text + vision | 256K | Use the model id in the `model` field of [chat completions](https://www.actava.ai/cura/docs/api). # Cura 1T model Source: https://www.actava.ai/cura/docs/cura-1t ## Overview Cura 1T targets three healthcare workloads: patient care (physician-rubric-graded communication), clinical reasoning over text and medical images, and agentic tasks — interactive diagnosis and EHR/FHIR tool execution. It leads frontier models on 5 of 6 healthcare benchmark panels and ranks second on MedXpertQA multimodal: ### Patient-facing communication Top scores on HealthBench Professional (0.662) and HealthBench Hard (0.368), both graded against physician-authored rubrics — the strongest capability gap vs the base model (+0.159 and +0.146). ### Clinical reasoning, text and image 60.0% on MedXpertQA text (best among frontier models) and 72.2% on the multimodal split — expert-level exam questions spanning 17 medical specialties, whose clinical images and case context are passed as `image_url` parts. ### Agentic EHR execution 94.0% on MedAgentBench (FHIR tool calls against a live EHR server) and 79.6% on AgentClinic's interactive diagnosis — use [tool calls](https://www.actava.ai/cura/docs/guides/tool-calls) to wire it to your systems. ## Example usage ### Install the OpenAI SDK `Shell` ``` pip install --upgrade "openai>=1.0" ``` ### Verify the installation `Shell` ``` python -c "import openai; print(openai.__version__)" ``` ### Quick start `Python` ``` import os from openai import OpenAI client = OpenAI( api_key=os.environ["ACTAVA_API_KEY"], base_url="https://inference.actava.ai/v1", ) response = client.chat.completions.create( model="actava/cura-soar", messages=[ {"role": "system", "content": "You are a clinical decision-support assistant."}, {"role": "user", "content": "A 68-year-old on apixaban needs dental extraction. Peri-procedural management?"}, ], temperature=1.0, ) print(response.choices[0].message.content) ``` ## Best practices - **Sample at temperature 1.0.** All published Cura 1T benchmark results were measured at T=1.0; it is the recommended default for clinical reasoning traces. - **Keep the full conversation.** For multi-turn diagnosis, resend the complete message history each turn — see [multi-turn chat](https://www.actava.ai/cura/docs/guides/multi-turn). - **Reuse long prefixes.** Stable system prompts and documents are served from prompt cache, skipping reprocessing. - **Human oversight.** Cura 1T is a research model — keep a clinician in the loop for anything patient-facing. ## Learn more - [API documentation](https://www.actava.ai/cura/docs) — quickstart, guides, and integrations - [Chat completions reference](https://www.actava.ai/cura/docs/api) # Streaming Source: https://www.actava.ai/cura/docs/guides/streaming ## How to stream ``` import os from openai import OpenAI client = OpenAI(api_key=os.environ["ACTAVA_API_KEY"], base_url="https://inference.actava.ai/v1") stream = client.chat.completions.create( model="actava/cura-soar", messages=[{"role": "user", "content": "Walk through sepsis screening criteria."}], stream=True, ) for chunk in stream: delta = chunk.choices[0].delta if chunk.choices else None if delta and delta.content: print(delta.content, end="", flush=True) ``` ## Wire format (no SDK) Each event is a `data:` line carrying one JSON chunk; content arrives in `choices[0].delta.content`. The stream terminates with `data: [DONE]`. `SSE` ``` data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":""}}]} data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Screen"}}]} data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]} data: [DONE] ``` ## Usage accounting Token usage for a streamed response is reported once, on the final chunk — read `usage` there rather than summing deltas yourself. ## Terminating early Close the HTTP connection (or break out of the SDK iterator) to stop generation; you are billed for tokens generated up to termination. Cap the worst case with `max_tokens`. # Multi-turn chat Source: https://www.actava.ai/cura/docs/guides/multi-turn ## The pattern `Python` ``` import os from openai import OpenAI client = OpenAI(api_key=os.environ["ACTAVA_API_KEY"], base_url="https://inference.actava.ai/v1") history = [ {"role": "system", "content": "You are a clinical decision-support assistant."}, ] def ask(user_text: str) -> str: history.append({"role": "user", "content": user_text}) response = client.chat.completions.create( model="actava/cura-soar", messages=history, ) reply = response.choices[0].message.content # Append the assistant turn so the next call sees the full conversation. history.append({"role": "assistant", "content": reply}) return reply print(ask("55-year-old male, 2 days of pleuritic chest pain. Differential?")) print(ask("D-dimer is 2.1. What next?")) ``` Interactive diagnosis works exactly this way — Cura 1T's AgentClinic results come from methodical multi-turn evidence-gathering, so let it ask follow-ups instead of front-loading everything into one prompt. ## Context budget The window is 256K tokens shared by history + new generation. For long encounters, truncate oldest turns first but keep the system prompt and any standing clinical context; summarize dropped history into a single assistant note when continuity matters. ## History and prompt caching Resending the same growing prefix is the textbook prompt-cache case: earlier turns are served from cache instead of reprocessed. Keep the prefix byte-stable — don't re-order or re-serialize old turns between calls. See [prompt caching](https://www.actava.ai/cura/docs/guides/caching). # Thinking mode Source: https://www.actava.ai/cura/docs/guides/thinking ## The thinking parameter | Field | Values | Behavior | | --- | --- | --- | | thinking.type | "enabled" (default) · "disabled" | Turns reasoning on or off for the request. | | thinking.keep | null (default) · "all" | Preserved Thinking: with "all", prior turns' reasoning stays in context — required for coherent multi-step tool use and long clinical workups. | ## Read the reasoning `Python` ``` from openai import OpenAI import os client = OpenAI(api_key=os.environ["ACTAVA_API_KEY"], base_url="https://inference.actava.ai/v1") response = client.chat.completions.create( model="actava/cura-soar", # Thinking is ON by default; shown here for clarity. Pass "disabled" to turn it off. extra_body={"thinking": {"type": "enabled"}}, messages=[ {"role": "user", "content": "A 62-year-old on warfarin reports dark stools. What should happen next?"}, ], ) msg = response.choices[0].message print(msg.reasoning_content) # the chain of thought print(msg.content) # the final answer ``` ## Preserved Thinking across turns With `keep: "all"`, send each assistant message back exactly as you received it — do not strip or edit `reasoning_content`: `Python` ``` # Multi-turn with Preserved Thinking: pass thinking.keep="all" and send every # historical assistant message back UNCHANGED, including its reasoning_content. messages = [ {"role": "user", "content": "Summarize this patient's anticoagulation risk."}, ] first = client.chat.completions.create( model="actava/cura-soar", extra_body={"thinking": {"type": "enabled", "keep": "all"}}, messages=messages, ) messages.append(first.choices[0].message) # keeps reasoning_content intact messages.append({"role": "user", "content": "Now draft the handoff note."}) second = client.chat.completions.create( model="actava/cura-soar", extra_body={"thinking": {"type": "enabled", "keep": "all"}}, messages=messages, ) ``` ## Notes - **Reasoning tokens are billed as output.** Budget `max_tokens` for the trace plus the answer. - **Truncation leaks the trace into content.** If `max_tokens` cuts generation off mid-reasoning (`finish_reason: "length"`), the partial trace comes back in `content` and `reasoning_content` is absent — check `finish_reason` before displaying `content`. - **Streaming:** deltas carry `reasoning_content` first, then `content`. # Vision Source: https://www.actava.ai/cura/docs/guides/vision ## Base64 upload The most common path for PHI-sensitive workloads: encode the image and inline it as a `data:` URI — nothing needs to be publicly hosted. ``` import base64 import os from openai import OpenAI client = OpenAI(api_key=os.environ["ACTAVA_API_KEY"], base_url="https://inference.actava.ai/v1") with open("cxr.png", "rb") as f: image_b64 = base64.b64encode(f.read()).decode() response = client.chat.completions.create( model="actava/cura-soar", messages=[ { "role": "user", "content": [ {"type": "text", "text": "Describe the abnormality in this chest X-ray."}, { "type": "image_url", "image_url": {"url": f"data:image/png;base64,{image_b64}"}, }, ], } ], ) print(response.choices[0].message.content) ``` ## URL reference Alternatively pass an https URL the API can fetch: `JSON` ``` { "type": "image_url", "image_url": { "url": "https://your-host.example/scans/cxr-1042.png" } } ``` ## Best practices - **Mix text and images freely** — a content array can interleave several `text` and `image_url` parts (e.g. current + prior study for comparison reads). - **Ask a focused question per image.** "Describe the abnormality" outperforms an unconstrained "read this scan". - **Image tokens count as input tokens** and are billed at the standard input rate. - **De-identify before sending.** Strip PHI burned into image headers or overlays — Cura 1T is a research model. # Tool calls Source: https://www.actava.ai/cura/docs/guides/tool-calls ## The tool loop Declare functions in `tools`; when the model decides to act it responds with `tool_calls` instead of content. Execute each call, append a `tool`-role result, and call the API again — until the model answers in plain content. `Python` ``` import json import os from openai import OpenAI client = OpenAI(api_key=os.environ["ACTAVA_API_KEY"], base_url="https://inference.actava.ai/v1") tools = [ { "type": "function", "function": { "name": "create_lab_order", "description": "Create a lab order in the EHR", "parameters": { "type": "object", "properties": { "patient_id": {"type": "string"}, "panel": {"type": "string", "description": "e.g. CBC, BMP"}, }, "required": ["patient_id", "panel"], }, }, } ] messages = [{"role": "user", "content": "Order a CBC panel for patient 1234."}] while True: response = client.chat.completions.create( model="actava/cura-soar", messages=messages, tools=tools, ) message = response.choices[0].message messages.append(message) if not message.tool_calls: print(message.content) break for call in message.tool_calls: args = json.loads(call.function.arguments) result = create_lab_order(**args) # your implementation messages.append( { "role": "tool", "tool_call_id": call.id, "content": json.dumps(result), } ) ``` ## Returning results Each result message must reference the exact `tool_call_id` it answers, and the assistant message carrying the tool_calls must stay in the history — removing it is the usual cause of "tool_call_id not found" errors. Treat ids as opaque strings (they look like `functions.:`, not OpenAI's `call_…` style — don't parse or pattern-match them). `JSON` ``` { "role": "tool", "tool_call_id": "functions.create_lab_order:0", "content": "{\"status\": \"placed\", \"order_id\": \"SR-2210\"}" } ``` ## Notes - **Parallel calls:** a single response may carry several tool_calls — execute all of them and append one tool message per call before the next request. - **tool_choice:** `"auto"` (default) lets the model decide; `"none"` suppresses calls — treat it as advisory rather than a hard guarantee, and ignore any `tool_calls` you didn't ask for; `{"type":"function","function":{"name":"..."}}` forces one. - **Tokens:** tool definitions ride in the prompt and are billed as input tokens — keep descriptions tight, and stable across turns so they cache. - **Safety:** gate any write action (orders, referrals, documentation) behind human confirmation in your application layer. # Web search Source: https://www.actava.ai/cura/docs/guides/web-search ## Enable it Add a single built-in tool to your request. Unlike a custom function it needs no schema — just the type `builtin_function` and the reserved name `$web_search`. The tool runs outside the model's own reasoning, so disable thinking mode on the search turn. `Python` ``` import json import os from openai import OpenAI client = OpenAI(api_key=os.environ["ACTAVA_API_KEY"], base_url="https://inference.actava.ai/v1") tools = [{"type": "builtin_function", "function": {"name": "$web_search"}}] messages = [ {"role": "user", "content": "Who won the most recent F1 race, and when?"} ] while True: response = client.chat.completions.create( model="actava/cura-soar", messages=messages, tools=tools, # The built-in tool runs outside the model's own reasoning; disable # thinking mode for the search turn. extra_body={"thinking": {"type": "disabled"}}, ) message = response.choices[0].message messages.append(message) if not message.tool_calls: print(message.content) break # $web_search is executed by the Cura platform, not by you. Echo each call # back as a tool result — the platform runs the search and feeds the # results to the model on the next turn. for call in message.tool_calls: messages.append( { "role": "tool", "tool_call_id": call.id, "name": "$web_search", "content": call.function.arguments, # relay verbatim; do not execute } ) ``` ## The flow When the model decides to search, it responds with a `$web_search` tool call carrying the query it chose: `JSON` ``` { "role": "assistant", "content": "", "tool_calls": [ { "id": "functions.$web_search:0", "type": "function", "function": { "name": "$web_search", "arguments": "{\"query\": \"most recent F1 race winner\"}" } } ] } ``` You don't execute anything — append the assistant message and a `tool`-role message echoing the call's arguments back, then call the API again. The platform runs the search, injects the results, and the model replies grounded in what it found (`finish_reason: stop`). Keep the assistant message that carries the tool call in your history — dropping it is the usual cause of "tool_call_id not found" errors. ## Notes - **Tokens:** search results are added to the prompt on the turn that consumes them, so they count as input tokens in `usage.prompt_tokens`. - **No schema:** the built-in takes only `type` and `name`. The `$` prefix is reserved for platform-executed tools. - **Verify results:** web results are third-party and unvetted. Cura 1T is a research model, not a medical service, and not a substitute for a clinician — confirm anything clinical before acting on it. # JSON mode Source: https://www.actava.ai/cura/docs/guides/json-mode ## Example Always describe the schema you expect in the prompt — JSON mode guarantees syntax, your prompt defines the keys: `Python` ``` import json import os from openai import OpenAI client = OpenAI(api_key=os.environ["ACTAVA_API_KEY"], base_url="https://inference.actava.ai/v1") response = client.chat.completions.create( model="actava/cura-soar", response_format={"type": "json_object"}, messages=[ { "role": "system", "content": ( "Extract structured data. Respond in JSON with keys: " "chief_complaint (string), red_flags (string[]), triage_level (1-5)." ), }, { "role": "user", "content": "58F, crushing substernal chest pain radiating to left arm, diaphoretic, 30 min.", }, ], ) data = json.loads(response.choices[0].message.content) print(data["triage_level"]) ``` `Output` ``` { "chief_complaint": "crushing substernal chest pain radiating to left arm", "red_flags": ["radiation to left arm", "diaphoresis", "duration 30 minutes"], "triage_level": 1 } ``` ## Notes - **Mention "JSON" in your prompt.** The request succeeds either way, but if the messages never reference JSON output the object you get back is unpredictable — always state the format and keys explicitly. - **Truncation produces invalid JSON.** If `finish_reason` is `"length"`, the object was cut off — raise `max_tokens` rather than attempting repair. - **Validate downstream.** Syntax is guaranteed; clinical semantics are not — check required keys and ranges before acting on the values. # Prompt caching Source: https://www.actava.ai/cura/docs/guides/caching ## How it works When a request repeats a prompt prefix the server has recently seen — a long system prompt, a policy document, earlier conversation turns — those tokens are served from cache instead of being reprocessed. No configuration, no cache-control headers: it's automatic. The response reports what was cached: `JSON` ``` "usage": { "prompt_tokens": 8412, "completion_tokens": 310, "total_tokens": 8722, "prompt_tokens_details": { "cached_tokens": 8192 } } ``` ## Getting the most from it - **Put stable content first.** Caching matches prefixes, so order prompts as: system prompt → reference documents → conversation history → the new user turn. - **Keep the prefix byte-identical.** Any change — a timestamp, a reordered field — invalidates the match from that point on. - **Multi-turn conversations benefit automatically**: each request re-sends history, and the unchanged turns are cache hits (see [Multi-turn chat](https://www.actava.ai/cura/docs/multi-turn)). A clinical assistant with an 8K-token system prompt serving 1,000 requests/day serves over 8M input tokens/day from cache — with zero code changes, and every response tells you exactly how much was cached. # Automatic retry Source: https://www.actava.ai/cura/docs/guides/auto-reconnect ## Example `Python` ``` import time from openai import OpenAI import os client = OpenAI(api_key=os.environ["ACTAVA_API_KEY"], base_url="https://inference.actava.ai/v1") def chat_once(messages): response = client.chat.completions.create(model="actava/cura-soar", messages=messages) return response.choices[0].message.content def chat(user_input: str, max_attempts: int = 5) -> str | None: messages = [{"role": "user", "content": user_input}] for attempt in range(max_attempts): try: return chat_once(messages) except Exception as exc: # 429 / 5xx / network blips are usually transient — back off and retry. wait = min(2 ** attempt, 30) print(f"attempt {attempt + 1}/{max_attempts} failed: {exc} — retrying in {wait}s") time.sleep(wait) return None ``` ## Notes - **Retry only transient errors.** Back off on `429` and `5xx`; don't retry `400/401/404` — those need a code or key fix (see [Errors](https://www.actava.ai/cura/docs/errors)). - **Streaming:** if a stream drops mid-response, re-issue the whole request — partial streams can't be resumed. - **Idempotency:** batch pipelines should key work by your own ids so a retry can't double-process a case. # Prompt best practices Source: https://www.actava.ai/cura/docs/guides/prompt-best-practices ## Structure the system prompt Role, task, rules, output contract — in that order. Put non-negotiables (safety behavior, what to do when information is missing) in the rules, not the user turn: `System prompt` ``` You are a clinical documentation assistant for licensed clinicians. ## Task Summarize the encounter note the user provides. ## Rules - Use only facts present in the note; never infer diagnoses. - Flag any medication-allergy conflicts under a "Safety" heading. - If information is missing, say what is missing instead of guessing. ## Output Markdown with headings: Summary, Medications, Safety, Follow-up. ``` ## Delimit injected content Wrap documents, notes, and retrieved passages in explicit markers so instructions and data can't blur: `Python` ``` messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": "Note:\n\n" + encounter_note + "\n\n\nQuestion: draft the follow-up plan."}, ] ``` ## Checklist - **One task per request.** Chain calls rather than stacking extraction + reasoning + formatting into one mega-prompt. - **Show, don't describe, formats.** A single worked example (few-shot) beats a paragraph of format prose; for machine-readable output use [JSON mode](https://www.actava.ai/cura/docs/guides/json-mode). - **Temperature 1.0 is the default and the recommendation** for reasoning-heavy clinical work; lower it only for deterministic formatting tasks. - **Leave headroom for thinking.** Reasoning tokens count against `max_tokens` — budget generously or answers truncate mid-plan. - **Stable prefixes cache.** Keep the long, shared part of the prompt (system + document) byte-identical across calls so it's [served from cache](https://www.actava.ai/cura/docs/guides/caching). # Benchmarking Source: https://www.actava.ai/cura/docs/guides/benchmarking ## Reference configuration `Python` ``` response = client.chat.completions.create( model="actava/cura-soar", temperature=1.0, # the published protocol; do not benchmark at T=0 max_tokens=32768, # room for the reasoning trace + the answer extra_body={"thinking": {"type": "enabled"}}, messages=[...], ) ``` ## Rules of the road - **Don't benchmark thinking models greedily.** Long-reasoning models can degenerate at near-zero temperature; `T=1.0` is both the published setting and the stable one. - **Never cap the trace.** A truncated answer (`finish_reason: "length"`) scores as a wrong answer — that measures your token budget, not the model. - **Generous timeouts.** Hard clinical questions produce long traces; per-example wall-clock limits that clip them mis-score capability as failure. - **Report the protocol.** Temperature, sampling count (pass@1 vs pass@k averaged over seeds), token budget, and harness — the same discipline as the [published evaluation notes](https://www.actava.ai/cura#benchmarks). # OpenAI compatibility Source: https://www.actava.ai/cura/docs/guides/openai-compatibility ## The two-line migration `Diff` ``` from openai import OpenAI client = OpenAI( - api_key=os.environ["OPENAI_API_KEY"], + api_key=os.environ["ACTAVA_API_KEY"], + base_url="https://inference.actava.ai/v1", ) response = client.chat.completions.create( - model="gpt-4o", + model="actava/cura-soar", messages=[...], ) ``` ## What's supported - `/v1/chat/completions` — messages (text + image parts), temperature, max_tokens, stream, tools / tool_choice, response_format - `/v1/models` — model listing - Standard `usage` accounting, including `prompt_tokens_details.cached_tokens` ## Differences to know - **Two endpoints only.** Files, batches, embeddings, images, and assistants routes are not served — requests to them return 404. - **One model id.** `actava/cura-soar`; any other value 404s. See [model list](https://www.actava.ai/cura/docs/models). - **Temperature default is 1.0** — also the setting used for all published benchmark results; there is no reason to lower it for clinical reasoning. - **Legacy functions/function_call still works, but answers in the modern shape** — requests using the deprecated fields are accepted, and the response carries `tool_calls` (not `function_call`). Write new code against `tools` / `tool_calls` (the OpenAI SDK has defaulted to it since v1). ## Framework configuration Any framework with OpenAI-compatible provider settings works the same way: set the base URL to `https://inference.actava.ai/v1`, the API key to your ACTAVA key, and the model to `actava/cura-soar`. # Claude Code Source: https://www.actava.ai/cura/docs/integrations/claude-code ## Step 1: Install CLIProxyAPI Install [CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI) with the install script, or run the container image: `Shell` ``` # Linux / macOS installer curl -fsSL https://raw.githubusercontent.com/router-for-me/cliproxyapi-installer/refs/heads/master/cliproxyapi-installer | bash # or run it in Docker docker run --rm -p 8317:8317 \ -v "$PWD/config.yaml":/CLIProxyAPI/config.yaml \ eceasy/cli-proxy-api:latest ``` ## Step 2: Connect it to Cura Register the Cura gateway as an OpenAI-compatible provider in `config.yaml`, then start the proxy. It listens on `127.0.0.1:8317` by default: `YAML` ``` # config.yaml port: 8317 openai-compatibility: - name: "cura" base-url: "https://inference.actava.ai/v1" api-key-entries: - api-key: "YOUR_ACTAVA_API_KEY" models: - name: "actava/cura-soar" alias: "cura-soar" ``` ## Step 3: Define the alias One shell alias points Claude Code at the proxy and pins both the main and subagent models to Cura 1T. Run `claudex` for a Cura-backed session; plain `claude` keeps its normal behavior. `Shell` ``` # ~/.zshrc (or ~/.bashrc) alias claudex='ANTHROPIC_BASE_URL=http://127.0.0.1:8317 \ ANTHROPIC_AUTH_TOKEN=sk-dummy \ CLAUDE_CODE_SUBAGENT_MODEL=cura-soar \ CLAUDE_CODE_ALWAYS_ENABLE_EFFORT=1 \ CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY=3 \ ENABLE_TOOL_SEARCH=false \ claude --model cura-soar' ``` ## Notes - **Why a proxy:** the Cura gateway exposes the Chat Completions API at `/v1` only. CLIProxyAPI translates Claude Code's Anthropic-protocol requests into Chat Completions calls locally — nothing extra leaves your machine. - **Keys stay in the proxy config.** Your ACTAVA API key lives in `config.yaml`; `ANTHROPIC_AUTH_TOKEN` can be a dummy value. If you set `api-keys` in the proxy config, pass one of those instead. - **The tuning flags are deliberate:** `MAX_TOOL_USE_CONCURRENCY=3` and `ENABLE_TOOL_SEARCH=false` keep the request shape conservative for a non-Anthropic backend, and `CLAUDE_CODE_SUBAGENT_MODEL` keeps subagents on Cura too. - **Agents loop; set budgets.** A coding agent can call the model many times per task — watch usage on long sessions. - Claude Code's and CLIProxyAPI's flags can change between versions; their own documentation is the source of truth for the client side. # OpenClaw Source: https://www.actava.ai/cura/docs/integrations/openclaw ## Setup - Install OpenClaw and complete its onboarding wizard. - Add the provider to your OpenClaw configuration: `JSON` ``` // openclaw.json — add Cura as a custom OpenAI-compatible provider { "models": { "providers": { "cura": { "baseUrl": "https://inference.actava.ai/v1", "apiKey": "${ACTAVA_API_KEY}", "api": "openai-completions", "models": [{ "id": "actava/cura-soar", "name": "Cura 1T" }] } }, "defaults": { "model": "cura/actava/cura-soar" } } } ``` Restart the gateway and send a test message from a connected channel. ## Notes - OpenClaw's config schema evolves quickly — match the provider block to the version you run; the constants are the base URL, key, and model id. - **Patient-facing channels:** if the agent talks to real people, keep the Cura safety framing in the system prompt — research model, not a medical service. # Hermes Agent Source: https://www.actava.ai/cura/docs/integrations/hermes ## Setup `Provider setup` ``` # In the provider wizard (hermes model), add a custom / OpenAI-compatible provider: Provider name: cura Base URL: https://inference.actava.ai/v1 API key: Default model: actava/cura-soar # Then start a session: hermes ``` ## Troubleshooting - **/model shows only one provider:** the in-chat switcher lists configured providers only — exit and run `hermes model` to add more. - **401 / auth errors:** re-check the key and that the base URL ends in `/v1`. - **Model-not-found:** the id is `actava/cura-soar` — including the `actava/` prefix. # Cline Source: https://www.actava.ai/cura/docs/integrations/cline ## Setup - Install Cline from the VS Code marketplace and open its settings. - Under **API Provider**, choose **OpenAI Compatible** and fill in: `Cline settings` ``` API Provider: OpenAI Compatible Base URL: https://inference.actava.ai/v1 API Key: Model ID: actava/cura-soar Supports Images: yes (Cura 1T is text + vision) Context Window: 256000 ``` Save, then run a small task to confirm the connection before pointing it at real work. ## Notes - Cline's setting names shift between versions — the constants are the base URL, the key, and the model id. - **Budget agent loops:** long autonomous edits can run many completions; watch usage. # RooCode Source: https://www.actava.ai/cura/docs/integrations/roo-code ## Setup - Install RooCode from the VS Code marketplace and open its provider settings. - Create a profile with: `RooCode settings` ``` API Provider: OpenAI Compatible Base URL: https://inference.actava.ai/v1 API Key: Model: actava/cura-soar ``` ## Notes - RooCode profiles are per-mode — set the Cura profile on the modes you want it to drive. - **Verify with a small task first**, and keep an eye on usage during long autonomous runs. # Chat completions Source: https://www.actava.ai/cura/docs/api ## Parameters | Name | Type | | Description | | --- | --- | --- | --- | | model | string | required | Model id. Use "actava/cura-soar". | | messages | array | required | Conversation so far. content is a string, or an array of parts for multimodal input (see Vision below). | | temperature | number | optional | Sampling temperature. Defaults to 1. | | max_tokens | integer | optional | Cap on generated tokens for this response — reasoning plus answer. Defaults to 32768, the model's output ceiling; larger values are accepted but capped, not rejected. | | stream | boolean | optional | When true, responds with server-sent events (see Streaming). | | thinking | object | optional | Reasoning controls: {"type": "enabled" | "disabled", "keep": null | "all"}. On by default (see the Thinking mode guide). | | tools | array | optional | Function definitions the model may call (see Tool calling). | | tool_choice | string | object | optional | "auto" (default), "none", or a specific function. | | response_format | object | optional | {"type": "json_object"} constrains output to valid JSON (see the JSON mode guide). | ## Vision Cura 1T reasons over clinical images natively. Pass images as `image_url` content parts — a `data:` URI (base64) or an https URL. `JSON` ``` { "model": "actava/cura-soar", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe the abnormality in this chest X-ray." }, { "type": "image_url", "image_url": { "url": "data:image/png;base64,iVBORw0KGgo..." } } ] } ] } ``` ## Tool calling Declare functions with the OpenAI tools shape; the model responds with `tool_calls` when it decides to invoke one. Return results as `tool`-role messages. Cura 1T is trained for EHR/FHIR tool workflows (MedAgentBench). `JSON` ``` { "model": "actava/cura-soar", "messages": [{ "role": "user", "content": "Order a CBC panel for patient 1234." }], "tools": [ { "type": "function", "function": { "name": "create_lab_order", "description": "Create a lab order in the EHR", "parameters": { "type": "object", "properties": { "patient_id": { "type": "string" }, "panel": { "type": "string", "description": "e.g. CBC, BMP" } }, "required": ["patient_id", "panel"] } } } ] } ``` ## Response Cura 1T reasons before answering, and thinking is on by default: `message.content` carries the final answer and `message.reasoning_content` the chain of thought. Reasoning tokens are billed as output and count toward `max_tokens` — see the [Thinking mode guide](https://www.actava.ai/cura/docs/guides/thinking) for disabling reasoning and preserving it across turns. `usage.prompt_tokens_details.cached_tokens` counts prompt tokens served from cache. Note: if generation is cut off while the model is still reasoning (`finish_reason` `"length"`), the partial trace is returned in `content` and `reasoning_content` is absent — check `finish_reason` before showing `content` to users. `200 OK` ``` { "id": "chatcmpl-...", "object": "chat.completion", "model": "actava/cura-soar", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "...", "reasoning_content": "..." }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 1204, "completion_tokens": 310, "total_tokens": 1514, "prompt_tokens_details": { "cached_tokens": 1024 } } } ``` ## Streaming With `"stream": true`, the response is a server-sent-event stream of `chat.completion.chunk` objects, terminated by `data: [DONE]`. Deltas carry `reasoning_content` first, then `content`. `SSE` ``` data: {"id":"chatcmpl-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant"}}]} data: {"id":"chatcmpl-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"reasoning_content":"The"}}]} data: {"id":"chatcmpl-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Escalate"}}]} data: {"id":"chatcmpl-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]} data: [DONE] ``` ## Errors | Status | Meaning | | --- | --- | | 401 | Missing or invalid API key. | | 404 | Unknown model id or route. | | 429 | Rate limit exceeded — back off and retry. | | 500 | Server error — retry with backoff. | `Error envelope` ``` { "error": { "message": "Invalid or expired API key", "type": "invalid_request_error", "param": null, "code": "invalid_api_key" } } ``` # Errors Source: https://www.actava.ai/cura/docs/errors ## Error envelope `JSON` ``` { "error": { "message": "Invalid or expired API key", "type": "invalid_request_error", "param": null, "code": "invalid_api_key" } } ``` ## 400 — Bad request The request body failed validation before reaching the model. - Malformed JSON, or a message missing role/content - An unsupported field — the validator rejects extra inputs (e.g. "Extra inputs are not permitted, field: 'messages[2].partial'") - An image_url part that is not a valid data: URI, or an https URL the gateway cannot download ## 401 — Authentication error The Authorization header is missing, malformed, or the key is invalid. - Missing 'Authorization: Bearer $ACTAVA_API_KEY' header - A revoked or mistyped key — check for stray whitespace when exporting the env var ## 403 — Permission error The key is valid but not entitled to this resource. - The key's grant doesn't include the requested model id ## 404 — Not found The route or model id doesn't exist. - A typo in the path — only /v1/models and /v1/chat/completions are served; any other route returns 404 with code NO_RULE_MATCHED - A model value other than "actava/cura-soar" (code model_not_found) ## 429 — Rate limit Too many requests or tokens in the current window. - Back off exponentially and retry; contact us for higher throughput commitments ## 500 / 502 / 504 — Server errors Transient service-side failures. - Retry with exponential backoff; if persistent, contact support with the request id - 502 upstream_error — the inference provider behind the gateway failed; retryable - An empty messages array currently hangs and times out as an HTML 504 rather than a fast 400 — always send at least one message ## Troubleshooting tips - Log the full error envelope, not just the HTTP status — the code disambiguates. - Retry only idempotent situations: 429 and 5xx with backoff. Don't retry 400s — fix the request. - For streaming requests, errors after the stream opens arrive as a terminal SSE event — handle both paths. # Changelog Source: https://www.actava.ai/cura/docs/changelog ## `2026-07`Cura 1T API launch - actava/cura-soar available via POST /v1/chat/completions and GET /v1/models - OpenAI-compatible request/response format; Python and Node SDKs work by changing base_url - Vision input (image_url parts), tool calling, and SSE streaming - Automatic prompt caching — repeated prompt prefixes are served faster - Contact-gated API keys