Completion¶
POST /v1/completion is the engine's front door. You give it an input and a mode; it enqueues a run and returns a job envelope. The run itself is asynchronous — read the result from /v1/jobs/{id}.
POST /v1/completion¶
Enqueue an engine run. Returns a pending JobStatus with an id of the form job-<uuid>.
Input: prompt or messages¶
Provide exactly one of these (supplying both or neither is a 422):
| Field | Type | Description |
|---|---|---|
prompt | string | A single instruction or question. Up to 500,000 characters. |
messages | array | An OpenAI-format chat history — a list of { "role", "content" } turns. The last turn must be role: "user". |
For messages, role is one of system, user, or assistant; the summed length of all content is capped at 500,000 characters (1–256 turns).
Body¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
prompt | string | one of | — | Single-string input (see above). |
messages | array | one of | — | Chat-history input (see above). |
mode | string | ✓ | — | Which orchestration to run. See Modes. |
main | string | — | resolved | The lead companion (name or cmp_<uuid>). If omitted, it is resolved from your preferences. |
participants | string[] | conditional | — | The companions to involve. Required for multi-persona modes unless supplied by a preference. |
stream | boolean | — | false | Hint that you intend to subscribe to the event stream. |
settings | object | — | — | Per-call overrides. See Settings. |
tools | array | — | — | Client-side tools the model may call. See Client tools. |
Companions and teams
A companion is a persona; a team groups companions. List the ones you can use with GET /v1/discover, and pass their names or ids as main / participants.
Modes¶
| Mode | What it produces |
|---|---|
answer | One companion's answer. |
answer_crumbs | One companion's answer, deepened over internal reasoning iterations. |
parallel | Every participant answers independently — N responses, no synthesis. |
parallel_with_main | Participants answer in parallel; a lead companion synthesizes them. |
panel | A two-stage expert panel that converges on a synthesized answer. |
discussion | A moderated, multi-turn discussion ending in a summary. |
Settings¶
settings is the single per-call override object. Every field is optional.
| Field | Type | Description |
|---|---|---|
model | string | Model slug for the answering personas. Must appear in GET /v1/models. |
temperature | number | Sampling temperature, range-checked against the model's bounds. |
top_p | number | Nucleus-sampling cutoff, range-checked against the model's bounds. |
crumbs | boolean | Toggle the reasoning-deepening loop. answer + crumbs: true runs as answer_crumbs. |
zdr | boolean | Per-call Zero Data Retention. When true, the run uses only non-retaining providers. Omit to inherit your account default. |
web_search | boolean | Per-call web search. When true, personas can search the web and cite live sources. Omit to inherit your account default. |
Examples¶
import os, requests
r = requests.post(
"https://api.humx.ai/v1/completion",
headers={"Authorization": f"ApiKey {os.environ['COMPANIONS_API_KEY']}"},
json={
"prompt": "What are the trade-offs of optimistic locking?",
"mode": "answer",
"settings": {"temperature": 0.4},
},
)
r.raise_for_status()
job = r.json()
print(job["id"], job["status"]) # job-... pending
const res = await fetch("https://api.humx.ai/v1/completion", {
method: "POST",
headers: {
Authorization: `ApiKey ${process.env.COMPANIONS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
prompt: "What are the trade-offs of optimistic locking?",
mode: "answer",
settings: { temperature: 0.4 },
}),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const job = await res.json();
console.log(job.id, job.status); // job-... pending
interface JobStatus {
id: string;
kind: "completion" | "companion" | "team" | "forensics";
status: "pending" | "running" | "complete" | "failed" | "requires_action";
content?: unknown;
}
const res = await fetch("https://api.humx.ai/v1/completion", {
method: "POST",
headers: {
Authorization: `ApiKey ${process.env.COMPANIONS_API_KEY!}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
prompt: "What are the trade-offs of optimistic locking?",
mode: "answer",
settings: { temperature: 0.4 },
}),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const job = (await res.json()) as JobStatus;
A multi-persona example — a parallel run across three companions:
{
"prompt": "Should we adopt event sourcing for the orders service?",
"mode": "parallel",
"participants": ["architect", "sre", "product-lead"]
}
Response¶
200 — a pending job envelope. The answer arrives later via /v1/jobs/{id}.
{
"type": "status",
"id": "job-1a2b3c4d-...",
"kind": "completion",
"status": "pending",
"mode": "answer"
}
Once complete, the same job carries a typed content keyed by shape:
{
"id": "job-1a2b3c4d-...",
"kind": "completion",
"status": "complete",
"content": {
"shape": "answer",
"companion": "Ada",
"response": "Optimistic locking trades..."
}
}
See Jobs → Result shapes for every mode's content.
Client tools¶
Attach a tools array to declare functions the model can call on your machine (available for answer and answer_crumbs). When the model calls one, the run pauses and hands the call back — the server never executes your tools. You run them and return the results via POST /v1/jobs/{id}/tool_outputs.
{
"prompt": "What's in my workspace?",
"mode": "answer",
"tools": [
{
"name": "read_user_file",
"description": "Read a file from the user's local workspace.",
"parameters": {
"type": "object",
"properties": {
"path": { "type": "string", "description": "Path relative to workspace root" }
},
"required": ["path"]
}
}
]
}
parameters is plain, provider-neutral JSON Schema. Bounds: up to 32 tools; each name matches ^[a-zA-Z_][a-zA-Z0-9_]*$ (1–64 chars, unique per request); parameters ≤ 16 KiB serialized. The full pause/resume loop lives on the Jobs page.
Validation¶
A 422 is returned before any run is enqueued when, for example:
type | Trigger |
|---|---|
prompt_or_messages | Both or neither of prompt / messages supplied. |
last_message_not_user | The final messages turn is not role: "user". |
messages_too_large | Summed content exceeds 500,000 characters. |
unknown_model | settings.model is not in your GET /v1/models allow-list. |
value_error (temperature / top_p) | The value is outside the model's published range. |
Insufficient balance surfaces as 402 when the run is charged.