# Tasks

A **task** is a procedure with steps that a Met runs. Unlike a Run — which is a one-off conversation — a task is stored, versioned, fires on its own and leaves a history of every execution.

Use the `tasks:read` and `tasks:write` scopes.

## The model in one sentence

**The task is the recipe; the execution is the dish.** You edit the task once; you run it as many times as you want, and each execution has its own state, its own history and its own pauses.

```ts
const task = await met.tasks.create({
  title: 'Qualify new leads',
  description: 'Review contacts with no stage and propose a rating.',
  agent_id: 12,
});

const execution = await met.tasks.execute(task.id);
// → { id, status: 'running', ... }
```
```python
task = met.tasks.create(
    title="Qualify new leads",
    description="Review contacts with no stage and propose a rating.",
    agent_id=12,
)

execution = met.tasks.execute(task["id"])
# → { "id": ..., "status": "running", ... }
```
```bash
met tasks run tsk_123 --wait
```

The execution starts in `running` and the steps run in the background: read
`met.tasks.execution(id)` to find out where it got to. From the terminal, `--wait`
waits for you and reflects the result in the exit code (see [CLI](cli.html)).

## Steps

Steps are what the Met does, in order. They are edited separately from the task so that reordering doesn't rewrite everything.

```ts
await met.tasks.steps.create(task.id, {
  title: 'Fetch contacts with no stage',
  type: 'agent',
});

await met.tasks.steps.reorder(task.id, [stepB.id, stepA.id]);
```

`steps.updateResult()` lets you write a step's result from the outside — useful when the real work was done by your system and you only want it on the record.

## Pauses for a person

This is what makes a task different from a script. A step can stop the execution and wait for someone: there are two ways, and they are **not the same**.

**Approval** — the Met did the work and asks permission to continue:

```ts
await met.tasks.approveStep(execution.id, step.id, 'Go ahead, the amounts check out');
await met.tasks.rejectStep(execution.id, step.id, 'That discount is not authorized');
```

Approving continues to the next step asynchronously. Rejecting stops that branch.

**Human step** — the work is done by the person, not by the Met:

```ts
await met.tasks.completeHumanStep(execution.id, step.id, 'Contract signed and filed');
```

Use it when your own system did what the step asked for: the executor moves on to the next one.

> All three operate on the **execution id**, not the task's. It is the most common mistake when integrating: the step belongs to the recipe, but the pause belongs to the run.

## Follow an execution

```ts
const history = await met.tasks.executions(task.id);
const state = await met.tasks.execution(execution.id);

await met.tasks.cancelExecution(execution.id);
```

`cancelExecution()` carries no `Idempotency-Key`: cancelling twice is harmless, and we don't want a retry to swallow the second cancellation.

To react live instead of polling, listen to the task events over SSE — see [Live events](events.html).

## Triggers

A trigger runs the task without anyone calling it: on a schedule, on a workspace event, or from an inbound webhook.

```ts
await met.tasks.triggers.create(task.id, {
  type: 'schedule',
  cron: '0 9 * * 1',        // Mondays 9am
});

await met.tasks.triggers.updatePrimary(task.id, { enabled: false });
```

`updatePrimary()` touches the task's primary trigger without you having to look up its id.

## Comments and attachments

The task thread accepts files. You upload first and send the metadata afterwards:

```ts
const attachment = await met.tasks.comments.upload(task.id, file, {
  filename: 'report.pdf',
  contentType: 'application/pdf',
});

await met.tasks.comments.create(task.id, {
  body: 'Attaching the closing report.',
  attachments: [attachment],
});
```

## Context and activity

```ts
const activity = await met.tasks.activity(task.id);            // for one task
const all = await met.tasks.workspaceActivity({ limit: 50 });  // for the whole workspace

const linked = await met.tasks.linkedItems(task.id);           // collection items
```

`linkedItems()` returns the collection items tied to the task: it is how a task works over concrete data instead of over nothing. See [Collections and items](collections-and-items.html).

## Saved views

Views belong to the **workspace**, not to a task: saved filters over the listing.

```ts
await met.tasks.views.create({ name: 'Blocked', filters: { status: 'blocked' } });
await met.tasks.views.reorder([viewA.id, viewB.id]);
```

## Status

```ts
await met.tasks.setStatus(task.id, 'paused');
```

Pausing a task does not stop the executions in flight: it prevents new ones from being born. To cut a run short, `cancelExecution()`.
