Tasks
Repeatable procedures a Met runs step by step, with pauses for a person to decide.
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.
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', ... }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", ... }met tasks run tsk_123 --waitThe 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).
Steps
Steps are what the Met does, in order. They are edited separately from the task so that reordering doesn't rewrite everything.
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:
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:
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
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.
Triggers
A trigger runs the task without anyone calling it: on a schedule, on a workspace event, or from an inbound webhook.
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:
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
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.
Saved views
Views belong to the workspace, not to a task: saved filters over the listing.
await met.tasks.views.create({ name: 'Blocked', filters: { status: 'blocked' } });
await met.tasks.views.reorder([viewA.id, viewB.id]);
Status
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().