Idempotency-Key header. The same key and request
return the original run without another enqueue. A changed request returns
409 idempotency_key_reused. The key is scoped to your organization, environment, and API key,
and remains reserved as long as the run row exists.
Flow
Persistrun_id. Polling remains the canonical reconciliation path even when using
webhooks.
What you send
input is the subject’s profile and run context. offerings is one entry per provider per requirement category.
input.current_coverage describes existing state (e.g., incumbent products or services). It provides context for scoring but does
not replace the offerings array.Building offerings from your data
Each provider entry becomes one offering per requirement category. If you have 3 providers each covering 3 categories, that’s 9 offerings (3 providers x 3 categories). Keepofferings[*].category aligned with input.coverage_types. Extra offerings for categories
not in coverage_types are ignored.
Multi-provider input
When comparing packages from multiple providers, submit offerings from each. The engine evaluates all valid combinations: both single-provider bundles and mixed-provider packages:Example: Insurance: Personal Lines. The
input and offerings schema depends on your engine’s configuration. This example shows a personal-lines insurance placement engine.Polling
PollGET /api/v1/runs/{run_id} until terminal. Start at 2s delay, backoff 1.5x, cap at 30s.
The terminal set is exactly
completed, degraded, and failed. A degraded
run is terminal. Never keep polling it.
Showing a result before the run finishes
A placement run finishes all of its deterministic scoring before it calls the model, and it persists that answer at the boundary. From that moment the response carriescompleted_deterministic_at plus output and solutions,
marked output_phase: deterministic_draft, while status is still
processing. Terminal responses carry output_phase: final.
A draft is a real answer over part of the decision, not a preview. Its ranks,
cluster labels, and overall_level values are real, but they are computed over
the deterministic scoring dimensions only, with the weights renormalized over
them. The model-scored dimensions are absent from a draft scorecard rather than
empty. They carry real weight, so the final answer can reorder the shortlist.
The polling loop above does not change: it still exits only on completed,
degraded, or failed. Render the draft from inside the loop instead.
output_phase: final.
A failed run carries no output and no solutions, and therefore no
output_phase, even when it reached the boundary before it failed. Webhook
payloads carry neither new field: every webhook event type is terminal, so
polling is the only surface that serves a draft. Curl and TypeScript versions
of this loop are on the Get Run Status
page.
Reading results
Key fields on a completed response:
There is no
solutions[*].carrier_name: use offerings[*].provider_name.
There is no solutions[*].score: use scorecard.overall_level.
Treat solutions[*].rank as the ordering contract.
Failure cases
completed with 0 solutions: output.diagnostic explains why. Common causes:
coverage_types / offerings[*].category mismatch, missing required categories, or all offerings
filtered out. This is a valid terminal state, not an error.
failed: read error and error_details. Terminal.
degraded: read the output, warnings when present, and any documented
engine-specific diagnostics. Terminal.
Stuck in processing: the EVALUATE stage can take time. Use backoff, don’t tight-loop.
Integration checklist
offeringsgenerated per category per provider, not per packageofferings[*].categoryaligns withinput.coverage_types- Polling treats
completed,degraded, andfailedas terminal - A rendered
deterministic_draftis labeled provisional and never ends the poll - Package score read from
scorecard.overall_level - Provider identity read from
offerings[*].provider_name - Both request and terminal response persisted
Reconciliation doctrine
Persist therun_id returned by submission. It is stable for the life of the
run and is the identifier used by polling, webhook payloads, delivery history,
and support. Webhooks are notifications, not the only source of truth. After a
missed, duplicate, delayed, or out-of-order callback, reconcile by polling
GET /api/v1/runs/{run_id} with that same ID.
Example artifacts
- Canonical request: complete
POST /api/v1/engines/default/runsbody withinputandofferings(insurance: personal lines) - Canonical response: complete
GET /api/v1/runs/{run_id}response for a completed run (insurance: personal lines)