Async solves and webhooks
Asynchronous solves and webhooks
Execution mode
Route optimization is measured in minutes rather than seconds, so asynchronous execution is the
default. mode defaults to auto, which selects synchronous execution only where the problem size
permits completion well within gateway timeouts.
An accepted job returns 202:
{
"request_id": "req_…",
"billing": { "unit": "node", "quantity": 412, "counted": true },
"data": {
"job": {
"id": "job_01j8q…",
"status": "queued",
"poll_after_ms": 1000,
"result_url": "/v1/jobs/job_01j8q…/result"
}
},
"meta": { "mode": "async" }
}
Charges apply to accepted work rather than to collection: the solve executes whether or not the result is retrieved. Querying job status and collecting a completed result are not billed.
Result retrieval
- Polling —
GET /v1/jobs/{id}untilstatusis terminal, thenGET /v1/jobs/{id}/result. - Long polling — the same status endpoint with
wait, which reduces request volume compared with a tight polling loop. - Webhook — supply
callback.urlon the original request; the result is delivered to that endpoint.
Webhooks
Callback endpoints must be verified before use. POST /v1/webhooks/verify performs a handshake
against the supplied URL. An unverified URL is rejected at job submission rather than after the
solve has been performed and billed.
Each delivery carries the following headers:
X-Open-Signature: v1=<hex hmac_sha256 of "<timestamp>.<raw body>">
X-Open-Timestamp: 1755950400
X-Open-Delivery: dlv_01j8q…
Receiver requirements:
- Return 2xx promptly. Perform downstream processing asynchronously. Deliveries time out after ten seconds.
- Verify the signature before trusting the payload.
- Deduplicate on the delivery identifier. Delivery is at-least-once.
- Do not assume ordering. Compare
occurred_at. - Fall back to polling if no callback is received within the retry horizon.
- Treat
result_urlas authoritative for large results; the inlinedatafield may be absent by design.
Failed deliveries are retried with backoff over approximately two and a half hours and are then
dead-lettered. All attempts are listed at GET /v1/jobs/{id}/deliveries, and an individual attempt
may be re-driven from the portal or via
POST /v1/jobs/{id}/deliveries/{deliveryId}/replay.
HTTP 400, 401, 403, 404, 410 and 422 end delivery at once; any other response or a connection error is retried. occurred_at is the time of this attempt; with CloudEvents or Standard Webhooks it is the time of the event, the same on every retry, and the X-Open-Job header carries the job id; attempt counts the tries. A job may also ask for CloudEvents or Standard Webhooks: add callback.format (native, ce_binary or ce_structured) and callback.signature (native, or standard_webhooks, which requires callback.secret). The native headers are always sent, and the verification handshake always uses the native format.
Result retention
Results are retained for 24 hours, deliberately longer than the retry horizon so that a result cannot expire before its own final delivery attempt. Results are not retained beyond that window and should be persisted by the client.