Async jobs
Track a long-running solve and collect its result.
platform.jobs
Stable
free
How to call it
REST
GET
/v1/jobs
GET
/v1/jobs/{jobId}
POST
/v1/jobs/{jobId}/cancel
GET
/v1/jobs/{jobId}/deliveries
POST
/v1/jobs/{jobId}/deliveries/{deliveryId}/replay
GET
/v1/jobs/{jobId}/result
POST
/v1/webhooks/verify
GraphQL
query
openJob
query
openJobs
query
openJobResult
query
openJobDeliveries
mutation
openJobCancel
mutation
openJobDeliveryReplay
mutation
openWebhookVerify
MCP
open_job_cancel
Cancel a queued or running job. A job that has already finished cannot be cancelled and its charge stands.
open_job_deliveries
Every webhook delivery attempt for one job — endpoint, outcome, attempt count and error. The first thing to check when a result never arrived. Free.
open_job_delivery_replay
Re-drive one webhook delivery by hand — the answer to a receiver that was down all afternoon. Free, and only for the caller's own deliveries.
open_job_get
Status and progress of one asynchronous job. Free — you have already paid for the work; asking whether it finished is not a second charge.
open_job_list
Recent asynchronous jobs for this client. Free.
open_job_result
Collect a finished job's result. Free, and available for 24 hours after the job completes.
open_webhook_verify
Run the challenge handshake against a callback URL so jobs may deliver to it. Must succeed before the URL can be used — a typo fails here rather than after an expensive solve. Free.
Request schema
Generated from the same validation rules the endpoint runs, so a field cannot be documented without being validated.
POST /v1/webhooks/verify
{
"type": "object",
"properties": {
"secret": {
"type": [
"string",
"null"
],
"maxLength": 255
},
"url": {
"type": "string",
"maxLength": 2048,
"format": "uri"
}
},
"additionalProperties": false,
"required": [
"url"
]
}
Request example
{
"url": "https://hooks.example.com/superroute",
"secret": "whsec_0123456789abcdef"
}
Response
What a successful call returns: the schema of `data` and `meta`, with a description of each field, followed by an example response from a real call.
GET /v1/jobs
Response schema
{
"description": "Recent jobs of this client, newest first.",
"data": {
"type": "object",
"properties": {
"jobs": {
"type": "array",
"items": {
"type": "object",
"description": "A job. Fields without a value are left out.",
"properties": {
"id": {
"type": "string",
"description": "The job id, `job_...`."
},
"status": {
"type": "string",
"enum": [
"queued",
"running",
"succeeded",
"failed",
"cancelled",
"expired"
]
},
"capability": {
"type": "string",
"description": "The capability the job runs."
},
"progress": {
"type": "integer",
"description": "Percent complete, 0-100."
},
"stage": {
"type": "string",
"description": "What the job is doing: `queued`, `solving`, an engine stage while it runs, then `done`, `failed` or `cancelled`."
},
"created_at": {
"type": "string",
"description": "ISO 8601 with offset."
},
"started_at": {
"type": "string",
"description": "ISO 8601 with offset. Absent until the job starts."
},
"finished_at": {
"type": "string",
"description": "ISO 8601 with offset. Absent until the job ends."
},
"result_url": {
"type": "string",
"description": "Where to collect the result. Present only once the job has succeeded."
},
"result_expires_at": {
"type": "string",
"description": "After this the result is deleted; collect it before then. ISO 8601 with offset."
},
"error": {
"type": "object",
"description": "Present only when the job failed.",
"properties": {
"code": {
"type": "string",
"description": "An error code from the error catalog."
},
"message": {
"type": [
"string",
"null"
],
"description": "What went wrong."
}
}
}
},
"required": [
"id",
"status"
]
}
}
}
}
}
Response example
{
"request_id": "req_01k8zq3m5n7p9r1t3v5x7z9b2d",
"data": {
"jobs": [
{
"id": "job_01k8zq4a6c8e0g2j4l6n8q0s2u",
"status": "succeeded",
"capability": "routing.optimize",
"progress": 100,
"stage": "done",
"created_at": "2026-10-23T09:27:40-04:00",
"started_at": "2026-10-23T09:27:40-04:00",
"finished_at": "2026-10-23T09:27:40-04:00",
"result_url": "/v1/jobs/job_01k8zq4a6c8e0g2j4l6n8q0s2u/result",
"result_expires_at": "2026-10-24T09:27:40-04:00"
}
]
}
}
GET /v1/jobs/{jobId}
Response schema
{
"description": "One job and where it has got to. Poll this until `status` is terminal (succeeded, failed, cancelled or expired), then collect `result_url`. `?wait=<seconds>` holds the request until the job ends or the wait runs out.",
"data": {
"type": "object",
"description": "A job. Fields without a value are left out.",
"properties": {
"id": {
"type": "string",
"description": "The job id, `job_...`."
},
"status": {
"type": "string",
"enum": [
"queued",
"running",
"succeeded",
"failed",
"cancelled",
"expired"
]
},
"capability": {
"type": "string",
"description": "The capability the job runs."
},
"progress": {
"type": "integer",
"description": "Percent complete, 0-100."
},
"stage": {
"type": "string",
"description": "What the job is doing: `queued`, `solving`, an engine stage while it runs, then `done`, `failed` or `cancelled`."
},
"created_at": {
"type": "string",
"description": "ISO 8601 with offset."
},
"started_at": {
"type": "string",
"description": "ISO 8601 with offset. Absent until the job starts."
},
"finished_at": {
"type": "string",
"description": "ISO 8601 with offset. Absent until the job ends."
},
"result_url": {
"type": "string",
"description": "Where to collect the result. Present only once the job has succeeded."
},
"result_expires_at": {
"type": "string",
"description": "After this the result is deleted; collect it before then. ISO 8601 with offset."
},
"error": {
"type": "object",
"description": "Present only when the job failed.",
"properties": {
"code": {
"type": "string",
"description": "An error code from the error catalog."
},
"message": {
"type": [
"string",
"null"
],
"description": "What went wrong."
}
}
}
},
"required": [
"id",
"status"
]
}
}
Response example
{
"request_id": "req_01k8zq3m5n7p9r1t3v5x7z9b2d",
"data": {
"id": "job_01k8zq4a6c8e0g2j4l6n8q0s2u",
"status": "succeeded",
"capability": "routing.optimize",
"progress": 100,
"stage": "done",
"created_at": "2026-10-23T09:27:40-04:00",
"started_at": "2026-10-23T09:27:40-04:00",
"finished_at": "2026-10-23T09:27:40-04:00",
"result_url": "/v1/jobs/job_01k8zq4a6c8e0g2j4l6n8q0s2u/result",
"result_expires_at": "2026-10-24T09:27:40-04:00"
}
}
POST /v1/jobs/{jobId}/cancel
Response schema
{
"description": "The job after the cancel. Cancelling a job that has already ended changes nothing and answers the job as it is.",
"data": {
"type": "object",
"description": "A job. Fields without a value are left out.",
"properties": {
"id": {
"type": "string",
"description": "The job id, `job_...`."
},
"status": {
"type": "string",
"enum": [
"queued",
"running",
"succeeded",
"failed",
"cancelled",
"expired"
]
},
"capability": {
"type": "string",
"description": "The capability the job runs."
},
"progress": {
"type": "integer",
"description": "Percent complete, 0-100."
},
"stage": {
"type": "string",
"description": "What the job is doing: `queued`, `solving`, an engine stage while it runs, then `done`, `failed` or `cancelled`."
},
"created_at": {
"type": "string",
"description": "ISO 8601 with offset."
},
"started_at": {
"type": "string",
"description": "ISO 8601 with offset. Absent until the job starts."
},
"finished_at": {
"type": "string",
"description": "ISO 8601 with offset. Absent until the job ends."
},
"result_url": {
"type": "string",
"description": "Where to collect the result. Present only once the job has succeeded."
},
"result_expires_at": {
"type": "string",
"description": "After this the result is deleted; collect it before then. ISO 8601 with offset."
},
"error": {
"type": "object",
"description": "Present only when the job failed.",
"properties": {
"code": {
"type": "string",
"description": "An error code from the error catalog."
},
"message": {
"type": [
"string",
"null"
],
"description": "What went wrong."
}
}
}
},
"required": [
"id",
"status"
]
}
}
Response example
{
"request_id": "req_01k8zq3m5n7p9r1t3v5x7z9b2d",
"data": {
"id": "job_01k8zq4a6c8e0g2j4l6n8q0s2u",
"status": "succeeded",
"capability": "routing.optimize",
"progress": 100,
"stage": "done",
"created_at": "2026-10-23T09:27:40-04:00",
"started_at": "2026-10-23T09:27:40-04:00",
"finished_at": "2026-10-23T09:27:40-04:00",
"result_url": "/v1/jobs/job_01k8zq4a6c8e0g2j4l6n8q0s2u/result",
"result_expires_at": "2026-10-24T09:27:40-04:00"
}
}
GET /v1/jobs/{jobId}/deliveries
Response schema
{
"description": "Every callback delivery of the job, with the last attempt's outcome.",
"data": {
"type": "object",
"properties": {
"deliveries": {
"type": "array",
"items": {
"type": "object",
"properties": {
"delivery_id": {
"type": "string",
"description": "The delivery id, `dlv_...`. Also sent in the `X-Open-Delivery` header."
},
"event": {
"type": "string",
"enum": [
"job.queued",
"job.progress",
"job.succeeded",
"job.failed",
"job.cancelled",
"job.expired"
],
"description": "The event delivered."
},
"status": {
"type": "string",
"enum": [
"pending",
"delivering",
"retrying",
"delivered",
"dead_letter"
],
"description": "`dead_letter` means every retry failed; replay it once the endpoint is fixed."
},
"endpoint_host": {
"type": "string",
"description": "The host the callback was sent to."
},
"attempt_count": {
"type": "integer",
"description": "Attempts made so far."
},
"http_status": {
"type": [
"integer",
"null"
],
"description": "The status your endpoint answered on the last attempt."
},
"response_time_ms": {
"type": [
"integer",
"null"
],
"description": "How long your endpoint took on the last attempt."
},
"error_code": {
"type": [
"string",
"null"
],
"description": "Why the last attempt failed, when it did."
},
"error_message": {
"type": [
"string",
"null"
],
"description": "Detail of `error_code`."
},
"last_attempt_at": {
"type": [
"string",
"null"
],
"description": "ISO 8601 with offset."
},
"next_attempt_at": {
"type": [
"string",
"null"
],
"description": "When the next retry is due. ISO 8601 with offset."
},
"delivered_at": {
"type": [
"string",
"null"
],
"description": "When your endpoint accepted it. ISO 8601 with offset."
}
}
}
}
}
}
}
Response example
{
"request_id": "req_01k8zq3m5n7p9r1t3v5x7z9b2d",
"data": {
"deliveries": [
{
"delivery_id": "dlv_01k8zq4h2k4m6p8r0t2w4y6a8c",
"event": "job.succeeded",
"status": "delivered",
"endpoint_host": "hooks.example.com",
"attempt_count": 1,
"http_status": 200,
"response_time_ms": 0,
"error_code": null,
"error_message": null,
"last_attempt_at": "2026-10-23T09:27:40-04:00",
"next_attempt_at": null,
"delivered_at": "2026-10-23T09:27:40-04:00"
}
]
}
}
POST /v1/jobs/{jobId}/deliveries/{deliveryId}/replay
Response schema
{
"description": "The delivery is queued to be sent again, with its original payload and a new attempt count.",
"data": {
"type": "object",
"properties": {
"delivery_id": {
"type": "string",
"description": "The delivery that was queued again."
},
"status": {
"type": "string",
"enum": [
"pending"
],
"description": "The delivery is queued and is sent by the next delivery run."
}
}
}
}
Response example
{
"request_id": "req_01k8zq3m5n7p9r1t3v5x7z9b2d",
"data": {
"delivery_id": "dlv_01k8zq4h2k4m6p8r0t2w4y6a8c",
"status": "pending"
}
}
GET /v1/jobs/{jobId}/result
Response schema
{
"description": "The finished result, exactly as the synchronous call would have answered it: `data`, `warnings` and `meta` of the capability that ran (this example is a routing.optimize job). `billing.counted` is false because the call was billed when the job was accepted. A job that has not succeeded answers 409 JOB_NOT_FINISHED.",
"data": {
"type": "object",
"description": "The capability's own `data`; see that capability's reference.",
"properties": {},
"additionalProperties": true
}
}
Response example
{
"request_id": "req_01k8zq3m5n7p9r1t3v5x7z9b2d",
"billing": {
"unit": "node",
"quantity": 5,
"counted": false,
"breakdown": {
"nodes": 5,
"jobs": 3,
"shipments": 0,
"shipment_nodes": 0,
"vehicles": 2,
"vehicle_nodes": 2
}
},
"data": {
"routes": [
{
"vehicle_id": "van-2",
"stops": [
{
"type": "start",
"arrival": "2026-11-02T08:31:07-05:00",
"arrival_epoch": 1793626267,
"distance_meters": 0,
"duration_seconds": 0,
"load": [
6
]
},
{
"type": "job",
"arrival": "2026-11-02T08:35:11-05:00",
"arrival_epoch": 1793626511,
"distance_meters": 1839,
"duration_seconds": 244,
"leg_distance_meters": 1839,
"leg_duration_seconds": 244,
"id": "order-1001",
"load": [
4
]
},
{
"type": "job",
"arrival": "2026-11-02T08:47:56-05:00",
"arrival_epoch": 1793627276,
"distance_meters": 5338,
"duration_seconds": 709,
"leg_distance_meters": 3499,
"leg_duration_seconds": 465,
"id": "order-1003",
"load": [
3
]
},
{
"type": "job",
"arrival": "2026-11-02T09:00:00-05:00",
"arrival_epoch": 1793628000,
"distance_meters": 9220,
"duration_seconds": 1193,
"leg_distance_meters": 3882,
"leg_duration_seconds": 484,
"id": "order-1002",
"load": [
0
]
},
{
"type": "end",
"arrival": "2026-11-02T09:08:17-05:00",
"arrival_epoch": 1793628497,
"distance_meters": 10687,
"duration_seconds": 1390,
"leg_distance_meters": 1467,
"leg_duration_seconds": 197,
"load": [
0
]
}
],
"stop_count": 5,
"task_count": 3,
"distance_meters": 10687,
"duration_seconds": 1390,
"service_seconds": 840,
"waiting_seconds": 0,
"geometry": null
}
],
"unassigned": [],
"summary": {
"routes": 1,
"assigned": 3,
"unassigned": 0,
"distance_meters": 10687,
"duration_seconds": 1390
}
},
"meta": {
"engine": "sroute2",
"engine_version": "1.6.1",
"solve_id": "e363b370-4113-44a0-8630-1579e1fbc799",
"capacity_dimensions": [
"0"
]
}
}
POST /v1/webhooks/verify
Response schema
{
"description": "Whether the endpoint answered the challenge. A job callback is accepted only for a URL verified this way: the endpoint receives `{\"event\": \"endpoint.verify\", \"challenge\": \"...\"}` and must answer 2xx with `{\"challenge\": \"<the same value>\"}`.",
"data": {
"type": "object",
"properties": {
"verified": {
"type": "boolean",
"description": "The endpoint echoed the challenge."
},
"reason": {
"type": [
"string",
"null"
],
"description": "Why verification failed; null when verified. `UNSAFE` (not a public HTTPS address), `UNREACHABLE` (no answer within the timeout), `HTTP_<status>` (the status your endpoint answered) or `CHALLENGE_MISMATCH`."
}
},
"required": [
"verified"
]
}
}
Response example
{
"request_id": "req_01k8zq3m5n7p9r1t3v5x7z9b2d",
"data": {
"verified": true,
"reason": null
}
}
What it costs
Never billed, and never disable-able.
Every billable response carries a billing block with the unit, the quantity and how the quantity was derived.
Errors it can return
| Status | Code | Description |
|---|---|---|
| 401 | SESSION_EXPIRED |
The session token is invalid or has expired. |
| 401 | TOKEN_INVALID |
Unknown or revoked API token. |
| 401 | TOKEN_MISSING |
No API token. Send Authorization: Bearer <your token>. |
| 403 | CAPABILITY_NOT_ENABLED |
This capability is not enabled for your account. See :doc to enable it. |
| 403 | CAPABILITY_NOT_GRANTED_TO_KEY |
Your account has this capability, but this API key was not granted it. |
| 403 | OPEN_API_DISABLED |
Open Platform access is not enabled for this account. |
| 404 | JOB_NOT_FOUND |
No such job, or it belongs to another account. |
| 409 | JOB_NOT_FINISHED |
The job has not finished yet. Poll the job, or wait for the callback. |
| 410 | JOB_RESULT_EXPIRED |
The result has expired and is no longer available. Submit the request again. |
| 422 | VALIDATION_FAILED |
The request failed validation. See details. |
| 429 | RATE_LIMITED |
Too many requests. Please retry after the indicated delay. |
| 500 | INTERNAL_ERROR |
An unexpected error occurred. Quote the request_id when contacting support. |