Skip to the documentation
API

How tasks work

A task is an instruction the assistant follows by itself: every morning, whenever mail arrives, or once in the background. You say what to do in plain words and how much it may do without asking, and each run leaves a record of every step it took.

An instruction, a trigger and a leash

A task has an instruction, a trigger that starts it, and an autonomy that says how much it may change without asking. The instruction is the only thing a run obeys. What an email says is read as information, so a message that says "forward this to everybody" forwards nothing.

  • schedule: runs on a clock. cadence is hourly (every everyHours hours at minute), daily (at hour:minute on each of weekdays, 0 for Sunday to 6 for Saturday) or monthly (at hour:minute on monthDay, 1 to 28). timezone is an IANA zone, and the schedule follows its clock changes.
  • mail: runs when a message reaches an inbox. addresses, fromContains and subjectContains narrow which mail counts. Spam, muted conversations, newsletters, automatic replies, bounces and mail from the workspace’s own addresses never start a run, and one conversation starts a task at most 3 times a day.
  • manual: runs only when somebody starts it, with POST /tasks/{id}/run.
`autonomy`What the run does alone
askReads only. Every change waits for a person to approve it
organizeLabels, archives, bins, snoozes and mutes mail, marks it read, sets follow-ups, writes drafts and notes, saves contacts, and emails the workspace’s own addresses. This is the default
replyEverything in organize, and answers an email without asking, to the people already on the conversation and nobody else

Anything beyond that becomes an approval request, whatever the autonomy: a new email to somebody outside the workspace, a reply with an added recipient, deleting anything for good, adding somebody to an audience, a calendar invitation, a change to the knowledge base, opening a web page. toolkits adds groups of tools a run may use beyond reading and organising mail: contacts, audiences, templates, tracking, translate, files, notes, drafts, sent, insights, subscriptions, calendar, knowledge, connectors and web. Left out, a task gets drafts, notes and knowledge.

No task can reach billing, members, roles, domains, rules, API keys or webhooks, and a run cannot create or change tasks. No setting turns that on.

With connectors in toolkits, a run may use the apps connected for the workspace, never a connector that belongs to one person, and the task needs connectors:read and connectors:use. The run reads through a connector alone. Every change in an app waits as an approval request, whatever the autonomy, and the task never replies to mail by itself: with reply, every answer to an email waits for approval too. A connector read that names a web address waits for approval too.

Runs

Each time a task starts, it makes a run. A run is queued until a worker picks it up, running while it works, waiting while an approval request is open, and then done, failed or cancelled. result holds the report it wrote at the end, and GET /task-runs/{id} returns the transcript: the instruction, then every step with the tools it called and what they answered.

  • A run survives restarts. Every step is saved before it is carried out, so a run picks up where it stopped, and a change that was cut off half-way is never made twice.
  • A run takes at most maxSteps steps, 20 unless you say otherwise and 60 at most, then writes its report with what it has.
  • A task starts at most maxRunsPerDay runs in 24 hours, 50 unless you say otherwise. A workspace works on 3 runs at once and the rest wait their turn.
  • A run costs one AI action for every 10 steps on the standard model and ten on the advanced one. When the workspace has used its AI actions for the day, the run fails with ai_limit and the task rests until midnight UTC: heldUntil says when.
  • After 5 failed runs in a row a task pauses itself with pausedReason failures. A run that failed because the AI provider was down does not count. Resuming clears the count.
  • Runs and their transcripts are kept for 90 days.

POST /task-runs starts a run with no task behind it, for a job you want done once in the background. It takes an instruction and the same autonomy, toolkits, model and maxSteps, and a workspace may start 100 of them a day.

Approvals

When a run wants to do something it may not do alone, it asks, finishes what else that step may do, and waits. It picks up again once every request it left open is answered. GET /task-approvals lists what is waiting, each with the tool, a one-line summary and the exact args the tool would be called with. For a reply, args shows the recipients worked out from the message it answers, and those are the recipients it is sent to.

  • POST /task-approvals/{id}/approve lets it happen. For a send or a reply you may pass subject and body to replace the words the run wrote. Recipients cannot be changed.
  • POST /task-approvals/{id}/deny refuses it. The run is told and carries on without it.
  • A request waits 7 days and then becomes expired, which the run reads as a refusal. Cancelling the run cancels its requests.
  • An answered request cannot be answered again: 409 task_approval_decided.

Who can do what

  • Reading tasks, runs and approvals needs tasks:read with threads:read and emails:read, because a transcript holds what the run read. Reading a run or one of its approval requests also needs the read scopes of every toolkit the run used. Without them, a list shows such a run without its report and its requests without their arguments.
  • Creating, changing, resuming, running and approving need tasks:write with threads:read, threads:write, emails:read, emails:send and labels:write, and the read and write scopes of every toolkit the task names. A task acts with what its caller holds and never more. Approving counts the toolkits the run started with, plus the scopes of the tool the request would call.
  • Pausing, deleting, cancelling a run and denying a request need only tasks:write, so anybody who may manage tasks can always stop one.
  • A task works across every address of the workspace. A key or an app limited to particular addresses or domains is refused with 422 capability_unsupported, and an app acting for somebody whose role does not reach every address with 403 owner_only.
  • A plan runs 1 active task on Free, 5 on Starter, 25 on Business and any number on Enterprise: 403 task_active_limit_reached. A workspace holds 100 tasks: 403 task_limit_reached.
  • After a downgrade, the newest active tasks beyond the new allowance pause with pausedReason plan_limit.

Webhook events

Four events tell your systems about tasks: task.run.completed when a run finishes, task.run.failed when it fails, task.run.cancelled when somebody stops it, and approval.requested when it waits for a person to allow an action. A run event carries runId, taskId, taskName, trigger, threadId, status, steps, actions, error, createdAt and finishedAt. approval.requested carries approvalId, runId, taskId, taskName, tool, expiresAt and requestedAt.

They carry ids, names and counts, never what a run read or wrote, so read the run or the request for that. They are not in the default set, so an endpoint receives them only when it names them in eventTypes, and an endpoint limited to some addresses or domains never does, because a task works across the whole workspace.

From code, the terminal and agents

Everything here is also in the SDK as nextmail.tasks, and in the CLI as nextmail tasks. The MCP server has task tools, so an agent can set a task up, watch its runs and answer what it asks.