Runs are asynchronous units of work. A successful POST /runs returns 202 Accepted immediately, then the run moves through pending, running, and a terminal state.
POST /runs
Create a run.
curl -X POST https://havilo.ai/api/v1/runs \
-H "Authorization: Bearer $HAVILO_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: run-create-001" \
-d '{
"input_type": "query",
"query": "list 3 startups in climate-tech"
}'Query Input
Use query when you want Havilo to understand the request and choose the steps.
{
"input_type": "query",
"query": "Find 10 seed-stage climate startups in Europe",
"config": {
"strict_mode": true,
"global_search": false
},
"callback_url": "https://your-server.example/webhooks/network-runs"
}| Field | Required | Description |
|---|---|---|
input_type | Yes | Must be "query". |
query | Yes | Natural-language request. |
config.strict_mode | No | Defaults to true. Ambiguous strict queries can fail before work starts. |
config.xray | No | Hydrates context from the global workspace alongside the caller's workspace. Requires the config:xray scope. |
config.global_search | No | Allows broader search when enabled for your key and workspace. Requires the config:global_search scope. |
config.unlimited_transform | No | Removes the ~100-entity cap on transform steps. Requires the config:unlimited_transform scope; runs without it are rejected with 403 forbidden_feature. |
callback_url | No | HTTPS URL for run webhook delivery. |
You may send the callback URL as either top-level callback_url or config.callback_url. If both are present, they must be identical. The resolved value is echoed as config.callback_url. The URL must be HTTPS and must not resolve to private IP ranges (RFC1918, loopback, link-local). See Webhooks for signature verification.
Plan Input
Use plan when you already know the exact steps.
{
"input_type": "plan",
"plan": {
"title": "Find and rank climate-tech startups",
"steps": [
{
"index": 0,
"kind": "fetch",
"title": "Fetch climate-tech startups",
"params": {
"source": "database",
"query": "climate-tech startups",
"limit": 50
}
},
{
"index": 1,
"kind": "filter",
"title": "Pick the top 3 by customer fit",
"instructions": "Keep startups with named paying customers in heavy industry.",
"params": {
"steps_to_filter": [0],
"limit_to": 3
}
},
{
"index": 2,
"kind": "reconstitute",
"title": "Format the result",
"params": {
"context_step_ids": [1],
"expected_artifacts": ["NONE"]
}
}
]
}
}fetch.params.limit is a retrieval cap: the fetch step returns at most that many rows or entities. For semantic ranking or top-N selection, fetch a broader candidate set and use filter.limit_to.
Each step's instructions say what it is for and are the only text its model reads; params say how it is wired; title and the optional description are for the person watching the run. filter steps require instructions — that is the only place a filter's criteria can be stated, and one submitted without them is rejected at creation. See Step Kinds.
Plan step params are validated when the run is created. Omitted optional params use the defaults listed in Step Kinds; unknown params are rejected so typos fail before work starts.
Supported public step kinds are fetch, vector_search, filter, transform, analyze, discover, reconstitute, and inject_context. See Step Kinds for the full parameter reference for each kind.
Preset Input
Use preset for a packaged workflow.
{
"input_type": "preset",
"preset": "dossier",
"params": {
"entity": {
"id": "550e8400-e29b-41d4-a716-446655440002"
},
"sections": ["background", "funding", "key_people"],
"max_tokens": 2000
}
}Only the dossier preset is available in this version. See Dossier Preset.
GET /runs/{id}
Fetch the current run envelope.
curl https://havilo.ai/api/v1/runs/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer $HAVILO_API_KEY"Run Envelope
| Field | Description |
|---|---|
id | Run UUID. |
object | Always "run". |
status | pending, running, paused, succeeded, failed, or canceled. paused is a transient state during scheduled maintenance; runs return to running automatically and require no client action. |
api_version | API version used for the run. |
created_at | Unix epoch milliseconds. |
completed_at | Unix epoch milliseconds. Present only for terminal runs. |
workspace_id | Workspace UUID. |
input | Echo of the accepted input. |
config | The config you sent, with callback_url resolved. Defaults apply but are not echoed. |
steps | Step summaries with public step kinds. |
message | Narrative answer when available. Records in it are record links. |
artifacts | Artifact metadata. Always present; empty when the run saved none. Fetch bodies with GET /artifacts/{id}. |
note_ids | IDs of notes produced by the run. Always present; empty when there are none. |
warnings | Non-fatal warnings. |
errors | Error objects for failed or partially failed runs. |
usage | Aggregated metrics for the run. See usage fields below. |
resumable | Whether the run can be resumed. |
request_id | Request correlation ID. |
usage fields
| Field | Description |
|---|---|
usage.calls | Successful physical provider calls recorded for this run. |
usage.unpriced_calls | Successful calls missing provider usage or a configured price. |
usage.input_tokens | Total input tokens across successful calls. |
usage.cached_input_tokens | Input tokens served from provider cache. |
usage.output_tokens | Total output tokens across successful calls. |
usage.thinking_tokens | Provider-reported thinking tokens. |
usage.cost_usd | Exact total of priced successful calls. |
usage.credits_at_most | The most the run's plan can cost, in credits. It is held against your credits when the run starts; a run submitted as a query holds 400 until its plan exists. Absent on a run that draws no credits. |
usage.credits_used | What the run was charged, in credits. null until it ends, 0 if it failed. Absent on a run that draws no credits. |
Usage belongs to the run, not to individual step records.
POST /runs/{id}/cancel
Cancel a run.
curl -X POST https://havilo.ai/api/v1/runs/550e8400-e29b-41d4-a716-446655440000/cancel \
-H "Authorization: Bearer $HAVILO_API_KEY"A queued run stops at once. A running run finishes the step it is on, which is billed, then ends
canceled; its run.canceled event arrives when that step does. If the run is already terminal, the
response returns the current terminal envelope.
Idempotency
Send an Idempotency-Key header on POST /runs to make retries safe.
- Same key and same body returns
200with that run's current envelope. A new create returns202. - Same key and different body returns
409 idempotency_key_conflict. - Use a unique key per logical create attempt.