Loading documentation

Access required

Your account doesn't have access to the documentation. Contact your administrator.

Sign out
Skip to main content

Ask a Model

Status: beta Category: ask Added: 2026-06-26 Last 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

FieldValue
MethodPOST
Path/api/ask/run
Full URLhttps://engine.tripod-health.com/api/ask/run
AuthenticationAPI key (required)
Request bodyapplication/json
Response bodyapplication/json
IdempotentNo (a model may return a different answer each call)
Rate-limitedYes

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

HeaderRequiredDescription
apikeyYesConsumer API key.
Content-TypeYesMust be application/json.
X-Request-IDNoOptional client-generated correlation ID. If omitted, one is generated and echoed in the X-Request-ID response header.

Query parameters

ParameterTypeRequiredDefaultDescription
modelstringYesThe 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

FieldTypeRequiredConstraintsDescription
promptstringYesnon-emptyThe free-text prompt to send to the model.
paramsobjectNoOptional per-call model parameters. Unknown keys are ignored.
params.max_tokensintegerNo1 ≤ n ≤ 4096, default 1024Upper bound on the model's output length. Values above the cap are clamped to it. Applies to Claude-backed models.
params.systemstringNoOptional 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

FieldTypeAlways presentDescription
modelstringYesEcho of the ?model= you requested — the model that answered.
outputstringYesThe 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).
usageobject | nullYesToken counts when the backend reports them, otherwise null. When null, the call is priced by bytes instead of tokens (see below).
usage.input_tokensintegerWhen usage is presentTokens the model read.
usage.output_tokensintegerWhen usage is presentTokens 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

CodeMeaningWhen
200OKModel answered and the output passed the outbound de-id scan.
400ValidationErrorMissing ?model=, unknown model, or missing/empty prompt. The field extra names the offender.
400DeIdentificationViolationThe prompt contained an identifier shape.
401UnauthenticatedMissing or invalid API key.
402PaymentRequiredInsufficient credits or an exhausted budget. See credits.md.
413PayloadTooLargeBody exceeds the 512 KB limit.
415UnsupportedMediaTypeContent-Type is not application/json.
429RateLimitedPer-consumer rate limit exceeded. See Retry-After.
500InternalErrorUnexpected server-side failure. Include request_id when reporting.
502UpstreamErrorThe model backend failed, or its output failed the outbound de-id scan.
503ServiceUnavailableA required dependency is temporarily unhealthy.
504UpstreamTimeoutThe 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 returns 400 DeIdentificationViolation. Keep any consumer-side identity mapping on your side.
  • Assuming a fixed price per call. This endpoint is variable-cost — read X-Credits-Used rather 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.

Change history

DateChange
2026-06-26Initial publication.
2026-06-26Pricing split into per-model input_cost_factor / output_cost_factor (input and output units priced separately).