Asynchronous tasks

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.failed or response.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 Gone with event_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.