Asynchronous tasks
- Normative
- Draft 1.0
- Updated
- View as Markdown
7.1 Create a task
A client creates a task by posting the response request it wants to run, wrapped in a task envelope. An Idempotency-Key header is recommended (section 9.2).
POST /lmsp/v1/tasks
Authorization: Bearer <token>
Idempotency-Key: 01J8M7C4Y3N5P8R2T6V9X1Z4BH
{
"type": "response",
"input": {
"model": "qwen3:14b-q4_k_m",
"input": [
{
"type": "message",
"role": "user",
"content": [
{
"type": "input_text",
"text": "Write a long report."
}
]
}
]
},
"on_restart": "fail"
}
| Field | Required | Meaning |
|---|---|---|
type |
Yes | response, the only task type in 1.0. |
input |
Yes | A response request, exactly as for POST /lmsp/v1/responses. Its stream field is ignored. |
on_restart |
No | fail (the default), retry or resume. See section 7.5. |
The service MUST persist the task before it answers. It then returns:
HTTP/1.1 202 Accepted
{
"id": "task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC",
"object": "task",
"type": "response",
"status": "queued",
"created_at": 1794200100,
"updated_at": 1794200100,
"response_id": "resp_01J8M8R5X4B9D2F6H1K3N7Q0ZW",
"events_url": "/lmsp/v1/tasks/task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC/events",
"on_restart": "fail",
"response": null,
"error": null
}
| Task field | Meaning |
|---|---|
id |
Task identifier, such as task_.... |
object |
Always "task". |
type |
response. |
status |
The current state (section 7.2). |
created_at, updated_at |
Times in seconds since the Unix epoch. |
response_id |
Id of the response the task produces. It is assigned when the task is created. |
events_url |
Path of the event stream. Present when the event_resume capability is true. |
on_restart |
The restart policy of the task. |
response |
The response object once status is succeeded, otherwise null. |
error |
A problem object (section 9.3) when status is failed, otherwise null. |
GET /lmsp/v1/tasks/{task_id} returns the task object. Clients that do not use event streams poll this endpoint. An unknown id gives 404 with task_not_found. A finished task looks like this:
{
"id": "task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC",
"object": "task",
"type": "response",
"status": "succeeded",
"created_at": 1794200100,
"updated_at": 1794200162,
"response_id": "resp_01J8M8R5X4B9D2F6H1K3N7Q0ZW",
"events_url": "/lmsp/v1/tasks/task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC/events",
"on_restart": "fail",
"response": {
"id": "resp_01J8M8R5X4B9D2F6H1K3N7Q0ZW",
"object": "response",
"created_at": 1794200100,
"status": "completed",
"model": "qwen3:14b-q4_k_m",
"output": [
{
"id": "msg_01J8M8R6A2C5E8G1J4M7P0S3VY",
"type": "message",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "Local model services make on-device inference a shared, discoverable utility..."
}
]
}
],
"usage": {
"input_tokens": 12,
"output_tokens": 1480,
"total_tokens": 1492
},
"error": null,
"incomplete_details": null
},
"error": null
}
7.2 State machine
queued --> loading --> running --> succeeded
| | |
| | +--> failed
| | +--> cancelling --> cancelled
| +--------------> failed
+--------------------------> cancelled
| State | Meaning | Next states |
|---|---|---|
queued |
Accepted and persisted, waiting to start. | loading, cancelled, failed |
loading |
The model is being loaded. | running, cancelling, failed |
running |
The model is generating. | succeeded, failed, cancelling |
cancelling |
Cancellation was requested, and the backend has not stopped yet. | cancelled |
succeeded |
Final. The response is complete. | none |
failed |
Final. | none |
cancelled |
Final. | none |
The terminal states are succeeded, failed and cancelled. A task in a terminal state MUST NOT change again. A task whose response ends as incomplete, for example at the token limit, is succeeded.
Every transition is atomic, so a task is never both cancelled and completed. Whichever transition is committed first wins.
7.3 Cancellation
POST /lmsp/v1/tasks/{task_id}/cancel
The service returns 200 OK and the current task object. A queued task becomes cancelled at once. A loading or running task becomes cancelling until the backend has stopped, and then cancelled.
Cancellation MUST be idempotent. Cancelling a task that is already cancelling or cancelled returns the current task and produces no new error.
Cancelling a task that has already succeeded or failed is answered with 409 and task_not_cancelable. An unknown id gives 404 with task_not_found.
7.4 Event streams
GET /lmsp/v1/tasks/{task_id}/events
Accept: text/event-stream
This endpoint is optional. It is available when the event_resume capability is true.
- The events are the response events of section 6.3, for the response that the task produces. Numbering and resumption follow section 6.4.
- The stream starts with
response.created, which is recorded when the task is accepted. It ends with one final event that matches the outcome:response.completed,response.incomplete,response.failedorresponse.cancelled. - A client that connects late first receives the events it missed, then live events. The service closes the stream after the final event.
- Events are kept for a retention period (section 11.2). Older events give
410 Gonewithevent_history_expired.
7.5 Restart semantics
on_restart says what happens to an unfinished task when the service restarts:
| Value | Behavior |
|---|---|
fail |
The default. The service marks the unfinished task failed, with the error code server_restarted. |
retry |
The service runs the task again from the beginning. |
resume |
The service continues from a snapshot. This is allowed only when the backend explicitly supports resumable snapshots. Otherwise the create request is rejected with 400 and unsupported_capability. |
A task that is cancelling when the service restarts completes its cancellation and becomes cancelled, whatever its policy.
retry is not recommended as a default, because the task may already have run tools or produced external side effects. A client that chooses retry MUST combine it with an Idempotency-Key and make its tool calls idempotent.