# Async tasks
> Agent task lifecycle, the Task envelope, polling, callbacks, and cancellation.

Agent skills that take longer than an HTTP turn return `202` + a Task envelope.
The task is queued, runs asynchronously, and can be observed by polling or
callbacks:

```text
queued → running → succeeded | failed | canceled
```

## Task envelope

The create response, `GET /v1/tasks/{task_id}`, and the callback POST body all return the **same structure**:

| Field | Description |
| --- | --- |
| `task_id` | `task_`-prefixed task ID; stable across idempotent retries |
| `status` | `queued / running / succeeded / failed / canceled` |
| `capability` | Capability name, e.g. `text_to_video` or `social_data_fetch` |
| `workload_type` | `model` for media/understanding, or `social_operation` for public social-data reads |
| `model` / `resolved_model` | Requested model (may be `auto`) / model that actually ran. Null on `social_operation` tasks |
| `social` | Present on social Tasks: `platform`, `operation_key`, `operation_name`, `schema_hash` |
| `input` | Normalized echo of the input |
| `output` | Artifacts; present only on `succeeded`. Structure documented on each capability page |
| `progress` | `{ percent, stage, message }`; meaningful only while running |
| `warnings[]` | Reserved; currently always an empty list |
| `error` | `{ code, message, retryable }`; present only on `failed` |
| `usage` / `billing` | Billing details → [Billing & usage](https://docs.beatra.ai/en/docs/mechanisms/billing) |
| `callback` | Callback delivery status → [Callbacks](https://docs.beatra.ai/en/docs/mechanisms/callbacks) |
| `metadata` | Reserved; accepted at creation, currently always `null` in responses |
| `created_at / started_at / completed_at` | Lifecycle timestamps (UTC) |
| `links` | `{ self, cancel }` |

## Polling, listing, and cancellation

| Operation | Endpoint | Description |
| --- | --- | --- |
| Get one | `GET /v1/tasks/{task_id}` | Returns the full envelope; poll at ~5s intervals until a terminal state |
| List | `GET /v1/tasks?limit=100` | Cursor-paginated summaries; **excludes** output/usage |
| Cancel | `POST /v1/tasks/{task_id}/cancel` | Idempotent; terminal tasks are returned as-is, and an in-flight stop succeeds only when confirmed |

Before remote execution starts, cancellation stops the task and releases its
prepaid credits. Once execution has started, Beatra returns `canceled` only
when the stop is confirmed. A `409` means execution continues: do not claim the
task stopped or promise a refund, do not submit a replacement, and continue polling
the same task to its terminal state.

MiniMax H3 can confirm cancellation only while its upstream task is still
`queued`. Once it is `running`, Beatra returns `409` and does not refund the
task; the upstream DELETE operation is not used because it would not represent
a confirmed cancellation.

## Explicit model vs `auto`

- An explicitly specified `model` is **never silently replaced**: if it does not
  support a requested parameter, the request fails immediately with
  `422 unsupported_option` naming the field.
- With `model: "auto"`, choosing and switching the concrete model is the
  platform's job by design; the terminal task's `resolved_model` records which
  model actually ran.

Public social-data tasks are prepaid like generation: one `beatra.social.execute`
creates one task and one lookup. After that lookup has started, cancel returns
`409`; keep polling the same task.

## When the generated files are slow to fetch

After the model finishes, Beatra downloads the generated files from the
provider and stores them before the task succeeds; meanwhile `progress.stage`
is `finalizing`. If the provider's file host keeps failing, Beatra tries again
over up to about 40 minutes, without generating again and without charging
again, and the task stays `running`. If the files still cannot be fetched, the
task fails with `model_output_invalid` and its credits are refunded. Keep
polling the same task: submitting the request again creates a second task that
is billed separately.

## Storing artifacts

Resource URLs in `output` are CDN addresses. **Copy them to your own storage promptly** after fetching results — do not treat them as permanent links.
