Ask a Model
Status:
betaCategory:askAdded:2026-06-26Last reviewed:2026-06-26
Summary
Send a free-text prompt to a named model and get its answer back plus the
token usage of the call. You pick the model with the ?model= query
parameter; that choice selects both the backend that answers and the price
you pay. Unlike every other endpoint, the credit cost of a call here is
variable — it scales with how much the model reads and writes (see
What a call costs).
This is the engine's one general-purpose model endpoint. The analytics endpoints (composites, instruments, TrinityAssist) each return a fixed, structured result; this one returns whatever the chosen model generates for your prompt.
Endpoint
| Field | Value |
|---|---|
| Method | POST |
| Path | /api/ask/run |
| Full URL | https://engine.tripod-health.com/api/ask/run |
| Authentication | API key (required) |
| Request body | application/json |
| Response body | application/json |
| Idempotent | No (a model may return a different answer each call) |
| Rate-limited | Yes |
Generated text passes the engine's outbound de-identification scan before it
is returned; identifier-shaped output fails the call with 502 UpstreamError
rather than being silently scrubbed.
Purpose
Use this endpoint when you need a model's free-text answer to a prompt and no
purpose-built endpoint covers it. The ?model= parameter chooses which model
runs — different models trade off quality, latency, and price, and each is
priced with its own per-model input and output cost factors.
The set of available model names and their cost factors is shown on the developer portal's pricing page. To check whether a given model is currently healthy, use model service health.
Authentication
Send a valid API key in the apikey header. A missing or unknown key returns
401 Unauthenticated at the gateway before the request reaches the engine.
See authentication.
Request
Headers
| Header | Required | Description |
|---|---|---|
apikey | Yes | Consumer API key. |
Content-Type | Yes | Must be application/json. |
X-Request-ID | No | Optional client-generated correlation ID. If omitted, one is generated and echoed in the X-Request-ID response header. |
Query parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
model | string | Yes | — | The model to run. Selects both the backend and the price. An unknown name returns 400 ValidationError. |
Body
{
"prompt": "In one sentence, what is occupational health?",
"params": {
"max_tokens": 256,
"system": "You are a concise assistant."
}
}
Body fields
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
prompt | string | Yes | non-empty | The free-text prompt to send to the model. |
params | object | No | — | Optional per-call model parameters. Unknown keys are ignored. |
params.max_tokens | integer | No | 1 ≤ n ≤ 4096, default 1024 | Upper bound on the model's output length. Values above the cap are clamped to it. Applies to Claude-backed models. |
params.system | string | No | — | Optional system instruction for Claude-backed models. |
params is passed through to the selected backend. The keys above apply to
Claude-backed models; an in-house model service receives params as-is and
honors whatever subset it supports.
De-identification requirement
The prompt rides in the request body and is scanned for identifiers on the
way in, exactly like every other endpoint. Do not put names, dates of birth,
SSN-shaped values, email addresses, phone numbers, or medical record numbers
in the prompt — a prompt containing an identifier shape returns
400 DeIdentificationViolation. Describe a subject structurally (age bracket,
occupation class, ICD category) rather than identifiably. See
conventions.md.
Response
Success — 200 OK
{
"model": "claude-haiku-4-5-20251001",
"output": "Occupational health is the field of public health focused on preventing work-related injury and illness and promoting the physical and mental well-being of workers.",
"usage": {
"input_tokens": 17,
"output_tokens": 35
}
}
Response fields
| Field | Type | Always present | Description |
|---|---|---|---|
model | string | Yes | Echo of the ?model= you requested — the model that answered. |
output | string | Yes | The model's answer. A string for Claude-backed models; for an in-house model service it is whatever that model returns under its output key (may be any JSON value). |
usage | object | null | Yes | Token counts when the backend reports them, otherwise null. When null, the call is priced by bytes instead of tokens (see below). |
usage.input_tokens | integer | When usage is present | Tokens the model read. |
usage.output_tokens | integer | When usage is present | Tokens the model wrote. |
There is no request_id or timestamp in the success body; read the
X-Request-ID response header to correlate a successful call. Error responses
do carry both in the envelope (see Status codes).
Status codes
| Code | Meaning | When |
|---|---|---|
200 | OK | Model answered and the output passed the outbound de-id scan. |
400 | ValidationError | Missing ?model=, unknown model, or missing/empty prompt. The field extra names the offender. |
400 | DeIdentificationViolation | The prompt contained an identifier shape. |
401 | Unauthenticated | Missing or invalid API key. |
402 | PaymentRequired | Insufficient credits or an exhausted budget. See credits.md. |
413 | PayloadTooLarge | Body exceeds the 512 KB limit. |
415 | UnsupportedMediaType | Content-Type is not application/json. |
429 | RateLimited | Per-consumer rate limit exceeded. See Retry-After. |
500 | InternalError | Unexpected server-side failure. Include request_id when reporting. |
502 | UpstreamError | The model backend failed, or its output failed the outbound de-id scan. |
503 | ServiceUnavailable | A required dependency is temporarily unhealthy. |
504 | UpstreamTimeout | The model call exceeded the engine's generation budget. |
Error envelope
All non-2xx responses use the uniform envelope:
{
"timestamp": "2026-06-26T17:32:10.812Z",
"error": "ValidationError",
"message": "unknown model: not-a-model",
"request_id": "8f2c3b1d-5e4a-4a9c-9b1e-2a7f1d3e4c5b",
"field": "model"
}
See errors.md for the full code list.
What a call costs
This is the engine's first variable-cost endpoint. The credit cost is not fixed per route — it depends on the size of the call and the model you chose:
in = input_tokens (or prompt bytes when usage is null — UTF-8 byte length)
out = output_tokens (or output bytes when usage is null)
credits = max(1, ceil(in × input_cost_factor + out × output_cost_factor))
Each model has two per-model prices — input_cost_factor (credits per input
unit) and output_cost_factor (credits per output unit), so a model can weight
the tokens it writes differently from the tokens it reads. See the
pricing page on the developer portal for
the current factors of each model. Token-based pricing is used whenever the
backend reports usage; the byte fallback applies only when it does not.
Worked example, using the usage from the success example above
(input_tokens 17, output_tokens 35) and illustrative factors of
input_cost_factor 0.3 and output_cost_factor 0.6:
credits = max(1, ceil(17 × 0.3 + 35 × 0.6)) = max(1, ceil(5.1 + 21.0)) = 27
The exact amount any call spent is returned on the response in
X-Credits-Used, and the dollar cost in X-Credits-Cost-USD. Treat those
headers as the source of truth for what you were charged — see
credits.md.
Examples
curl
curl -sS -X POST "https://engine.tripod-health.com/api/ask/run?model=claude-haiku-4-5-20251001" \
-H "apikey: $ENGINE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"In one sentence, what is occupational health?"}'
Expected response:
{
"model": "claude-haiku-4-5-20251001",
"output": "Occupational health is the field of public health focused on preventing work-related injury and illness and promoting the physical and mental well-being of workers.",
"usage": {
"input_tokens": 17,
"output_tokens": 35
}
}
curl — unknown model (400)
curl -sS -X POST "https://engine.tripod-health.com/api/ask/run?model=not-a-model" \
-H "apikey: $ENGINE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"hello"}'
{
"timestamp": "2026-06-26T17:32:10.812Z",
"error": "ValidationError",
"message": "unknown model: not-a-model",
"request_id": "8f2c3b1d-5e4a-4a9c-9b1e-2a7f1d3e4c5b",
"field": "model"
}
Python (requests)
import os, requests
resp = requests.post(
"https://engine.tripod-health.com/api/ask/run",
params={"model": "claude-haiku-4-5-20251001"},
headers={"apikey": os.environ["ENGINE_API_KEY"]},
json={"prompt": "In one sentence, what is occupational health?"},
timeout=60,
)
resp.raise_for_status()
data = resp.json()
print(data["output"])
print("credits:", resp.headers.get("X-Credits-Used"))
Node.js (native fetch)
const url = new URL("https://engine.tripod-health.com/api/ask/run");
url.searchParams.set("model", "claude-haiku-4-5-20251001");
const resp = await fetch(url, {
method: "POST",
headers: {
apikey: process.env.ENGINE_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({ prompt: "In one sentence, what is occupational health?" }),
});
if (!resp.ok) throw new Error(`Engine ${resp.status}: ${await resp.text()}`);
const data = await resp.json();
console.log(data.output);
console.log("credits:", resp.headers.get("X-Credits-Used"));
Intended use
- One-off free-text questions to a model where no purpose-built engine endpoint fits.
- Calls where you want to choose the model explicitly and accept usage-based pricing.
- Prototyping prompts before deciding whether a dedicated endpoint is warranted.
Mistaken use
- Sending patient identifiers in the
prompt. The prompt is de-identified inbound; an identifier shape returns400 DeIdentificationViolation. Keep any consumer-side identity mapping on your side. - Assuming a fixed price per call. This endpoint is variable-cost — read
X-Credits-Usedrather than hard-coding a number. - Using it for the structured analytics that a dedicated endpoint already produces (composites, instrument scoring, SOAP drafts). Those return a validated, fixed-cost result; this returns raw model output.
- Treating the model output as de-identified-safe to re-emit blindly. It passed an outbound scan for identifier shapes, but it is still generated text — review it before display where appropriate.
Rate limits
Per-consumer limits are enforced at the gateway; see
rate-limits.md. Responses include the standard
RateLimit-* headers.
Related
- credits.md — how variable-cost pricing works and where to find per-model cost factors.
- model-services-health.md — check whether a model is currently healthy.
- conventions.md — de-identification contract, request IDs, timestamp shape.
- errors.md — error envelope and code list.
Change history
| Date | Change |
|---|---|
| 2026-06-26 | Initial publication. |
| 2026-06-26 | Pricing split into per-model input_cost_factor / output_cost_factor (input and output units priced separately). |