Orders
An order is a request to execute a service. It tracks inputs, parameters, and outputs through the service's lifecycle.
An order is a single execution of a service. Create one with POST /orders and Relu:
- Downloads the input files (from presigned URLs you provide, or from platform-uploaded file IDs)
- Queues the order for processing by our AI
- Runs the service
- Stores the outputs and optionally uploads them to presigned URLs you provide
Creating an order
Use the generic route POST /orders and pass the service's UUID as service_id in the body:
{
"service_id": "<service-uuid from GET /services>",
"order_name": "case-001",
"input_data": {
"lower_jaw": { "external_download_url": "...", "extension": ".stl" }
},
"parameter_values": {
/* service-specific */
},
"callback_url": "https://your-app.com/relu-webhook"
}The exact input_data, output_data, and parameter_values keys depend on which service you're calling — fetch them from GET /services/{id}, or open the three-dots menu → View API Example on the service card in the platform UI to copy a ready-to-run snippet.
Instead of (or in addition to) listing parameter_values, you can reference a saved preset by passing preset_id. The order inherits the preset's parameters and output settings; any parameter_values you also send override the preset for those keys.
Per-service routes will be deprecated
Each service used to have a typed dedicated route (POST /orders/model-builder-v1, POST /orders/dies-v1, etc.). They still work and
remain in the OpenAPI spec for existing integrations, but they are slated for
deprecation. Use POST /orders for new integrations.
Lifecycle
The order's main job moves through main_job.job_status:
Submitted, accepted and queued/runningDone, outputs are availableFailed, something went wrong — see Order processing errors for the fields to surface to your usersCancelled, cancelled before it finished
Use POST /orders/job-statuses when you need to poll for completion, or receive a
callback. Status polling reads from Automate's database
and uses fewer resources than progress polling. Send between 1 and 100 valid
order IDs per request. The API returns 422 for an empty list, more than 100
IDs, or a malformed ID. Use POST /orders/progress only if your UI needs a
progress percentage.
Approval status
Approval status is separate from main_job.job_status. Set it after the main
job reaches Done:
PUT /orders/{order_id}/status?status=ApprovedScope required
update:ordersThe request has no JSON body. The API user also needs order:approve
permission on the order. Valid values are:
Approved, accept the completed result.Rejected, reject the result without creating feedback or a refund request.Unapproved, reset an approval or rejection while keeping the status history.
You can move directly between any of these values. Each request appends a
status record, and the latest record is the order's current approval status.
Changing the status emits an order.status callback.
To update several orders, use the bulk route. It checks each order separately and returns the successful updates and failures in one response:
PATCH /orders/status?status=Approved
Content-Type: application/json
{ "order_ids": ["<order_id_a>", "<order_id_b>"] }Use POST /feedback/ when a rejection should
also record feedback or request a refund.
Tracking progress
Start with POST /orders/job-statuses if you only need to know whether orders are
finished. It reads from Automate's database, so it is faster and uses fewer
resources. Each request accepts between 1 and 100 valid order IDs.
If your UI needs a progress percentage, use POST /orders/progress. This endpoint
may contact processing engines to refresh stale progress values, which uses
additional resources. Pass the order IDs in one request:
// POST /orders/progress
{ "order_ids": ["<order_id_a>", "<order_id_b>"] }The response maps each order ID to its progress info:
{
"progress": {
"<order_id_a>": {
"order_id": "<order_id_a>",
"main_job": {
"job_status": "Submitted",
"end_timestamp": null
},
"progress_job": {
"job_type": "Main",
"job_status": "Submitted",
"end_timestamp": null
},
"progress": 42,
"error": null
},
"<order_id_b>": {
"order_id": "<order_id_b>",
"main_job": {
"job_status": "Done",
"end_timestamp": "2026-09-02T10:24:00Z"
},
"progress_job": {
"job_type": "Recompute",
"job_status": "Submitted",
"end_timestamp": null
},
"progress": null,
"error": null
}
}
}Reading the response:
main_job.job_statusis the authoritative state of the original order.progress_jobidentifies the Main or Recompute job described byprogress.progressis a number: render it as a percentage. A processing job is held at99until it finishes. A terminal job can report100.progressisnull: the order is queued, or there's no estimate yet. Show "Queued", a spinner, or0%instead of treating it as a percentage.progress_jobisnull: an accessible order has no Main or active Recompute job. It remains in the response even when no percentage is available.- The order ID is absent from
progress: the order does not exist or you cannot access it. This endpoint does not return anerrorslist. UsePOST /orders/job-statusesif you need an error for each omitted ID.
Progress caps at 99% until Done
The engine reaches 100% slightly before Relu finishes ingesting the outputs,
so a still-processing job is capped at 99. Treat main_job.job_status == "Done" (or the callback) — not progress == 100
— as the signal that outputs are ready.
Progress tells you how far along an order is, not whether it's finished. To detect completion, use a webhook or poll the order's status.
Order permissions
By default, orders are visible to the organization that created them. You can share orders with individual users or via public links. Orders are always billed to the owner organization, no matter who else has access.