Tasks, autonomy and approvals
Run durable tasks that wait on people and resume on their own, decide per action type what needs a person's approval, and review, approve, edit and undo staged actions.
A task is a durable unit of work an assistant owns until it is finished, such as "Renew the Brightline Freight service contract". A task survives restarts, waits days for a reply, and resumes on its own. Every action it takes in the outside world passes through the user's autonomy settings, which decide whether the action runs immediately, after a review window, or only after a person approves it.
How it works
| Concept | Description |
|---|---|
| Task | The durable record: title, owner, assistant, status, plan and activity timeline. |
| Run | One execution of the task by Prime. A task has many runs over its life. |
| Activity | One entry on the task's timeline: a message in or out, a status change, an approval. |
| Alarm | A scheduled wake. Every waiting task holds one, so no task is stranded. |
| Approval | A batch of staged actions waiting for a person. The API calls it a cart. |
| Autonomy settings | Per user, one approval mode for each action type. They decide which actions need approval. |
create ──> pending ──process──> executing ──┬──> completed
│
┌─────────────────────────┤ needs a person or a time
v │
awaiting_user_input / awaiting_non_user_response / alarm_set / scheduled
│ ^
└── reply arrives, ───────┘
alarm fires, or
user sends /process
Reads, research and drafting happen inside a run. Side effects (a message to someone other than the user, a calendar change, a shared file, a phone call) are staged in a cart and released according to the user's autonomy settings.
Task statuses
| Status | Description |
|---|---|
pending | Created or woken, waiting for a run to start. |
executing | A run is in progress. |
awaiting_user_input | Needs the user. Reply with POST /tasks/:taskId/process. |
awaiting_non_user_response | Waiting on someone else, such as an email reply. waitingOnContacts names them. |
alarm_set | Wakes itself at alarm.wakeAt. |
subtasks_running | Waiting on its own subtasks. |
scheduled | Starts at scheduledFor. |
on_hold | Stopped by the user (POST /tasks/:taskId/stop) or held by the assistant. POST /tasks/:taskId/process resumes it. |
paused | Held by the system. |
message_received, conversation | A conversational thread at rest. A new message reopens it. |
deferred_access, failed_access | Held, or stopped, for missing calendar or account access. |
credits_blocked | Out of credits. Resumes when credits are restored. |
completed | Finished by the assistant. |
done_user_marked | Marked done by the user. |
handled_by_other | Another assistant took the thread. handledBy says which. |
error, abandoned, archived | Failed, given up, or archived. |
Every task has these guarantees:
- No silent waits. A task cannot enter
awaiting_user_input,awaiting_non_user_responseorsubtasks_runningwithout an active alarm. If nobody replies, the alarm wakes the task to follow up or report back. - Replies route home. A reply from a person the task is waiting on, on the channel the assistant used to reach them, is attached to that task and wakes it. The next run sees the full history.
- The owner decides when it is done. Only the owner can stop a task or mark it done. Marking a task done discards any cart it has pending.
A task resumes when any of these happens:
- A person it is waiting on replies.
- Its alarm fires.
- The user sends it a message with
POST /tasks/:taskId/process. - A staged cart is approved, cancelled or executed. The next run is told the outcome.
Create and run a task
All task calls act as a user. Pass X-User-Email with an account API key, or use a delegated SDK client. See Authentication.
Creating a task does not start it. Create the task, then call process.
curl https://api.agnt.ai/tasks \
-X POST \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]" \
-H "Content-Type: application/json" \
-d '{
"title": "Renew the Brightline Freight service contract",
"description": "Harborview'\''s MSA with Brightline Freight ends 2027-01-01. Get their 2027 terms.",
"assistant": "6702b4c1d2e3f4a5b6c7d8e9",
"modelTier": "medium"
}'
{
"ok": true,
"task": {
"id": "6702c3a9b8c7d6e5f4a3b2c1",
"type": "task",
"title": "Renew the Brightline Freight service contract",
"description": "Harborview's MSA with Brightline Freight ends 2027-01-01. Get their 2027 terms.",
"status": "pending",
"owner": "[email protected]",
"modelTier": "medium",
"createdBy": "user",
"pendingCart": null,
"alarm": null,
"createdAt": "2026-11-02T16:24:11.000Z",
"updatedAt": "2026-11-02T16:24:11.000Z"
}
}
| Name | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Short name for the task. |
assistant | string | Yes | Id of an assistant the user has. Another assistant returns 403. |
description | string | No | What the user wants done, in their words. |
status | string | No | pending (default) waits for process. scheduled starts on its own at scheduledFor. |
scheduledFor | string | No | ISO 8601 time, with status: "scheduled". |
modelTier | string | No | low, medium, high or ultra. Fixed after the first run. See Model tiers. |
skills | string[] | No | Skill names the task should use. |
followers | string[] | No | Email addresses of people following the task. |
Start the task. The same call sends the user's reply to a task in awaiting_user_input.
curl https://api.agnt.ai/tasks/6702c3a9b8c7d6e5f4a3b2c1/process \
-X POST \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]" \
-H "Content-Type: application/json" \
-d '{ "message": "Dana Okafor at [email protected] handles renewals. The current MSA is attached.", "files": [{ "fileId": "6702c804b5c6d7e8f9a0b1c2" }] }'
{
"ok": true,
"process": {
"success": true,
"taskId": "6702c3a9b8c7d6e5f4a3b2c1",
"executionId": "6702c3e4a5b6c7d8e9f0a1b2"
}
}
| Name | Type | Required | Description |
|---|---|---|---|
message | string | No | A message from the user, saved to the task before the run. |
files | object[] | No | Attachments already in the user's drive, as { "fileId" }. Files the user cannot read are dropped. |
The same flow with the TypeScript SDK:
import { AgntClient } from '@agnt-sdk/client';
const client = await AgntClient.create();
const jane = client.as('[email protected]');
const task = await jane.tasks.create({
title: 'Renew the Brightline Freight service contract',
assistant: '6702b4c1d2e3f4a5b6c7d8e9',
});
await jane.tasks.process(task.id, {
message: 'Dana Okafor at [email protected] handles renewals.',
});
Follow a task
Read the task for its current state, and its activities for the timeline.
curl https://api.agnt.ai/tasks/6702c3a9b8c7d6e5f4a3b2c1 \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]"
curl "https://api.agnt.ai/tasks/6702c3a9b8c7d6e5f4a3b2c1/activities?limit=50" \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]"
Activities are newest first, in { "ok": true, "activities": [], "hasMore": true, "cursor": "<cursor>" }. Page older entries with before=<cursor>, passing the cursor back unchanged. Each activity has a type and a createdAt, and a message activity has a message with role, content and platform.
| Field | Description |
|---|---|
status | Where the task is in its lifecycle. |
summary | What the assistant has done, once there is something to report. |
plan | The assistant's checklist, each item pending, done, skipped or blocked. |
waitingOnContacts | The people the task is waiting to hear from. |
alarm | wakeAt, headline (the line to show the user) and status. |
pendingCart | A summary of what is staged for approval, or null. |
effectiveApprovalMode, approvalLocks | The autonomy mode that applies right now, and any admin locks. Returned by GET /tasks/:taskId. |
The API reference lists every field.
Stop, finish and reopen a task
# Stop the current run. The task goes to on_hold.
curl -X POST https://api.agnt.ai/tasks/6702c3a9b8c7d6e5f4a3b2c1/stop \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]"
# Mark done. Status becomes done_user_marked and any pending cart is discarded.
curl -X POST https://api.agnt.ai/tasks/6702c3a9b8c7d6e5f4a3b2c1/mark-done \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]"
# Reopen a done task. The assistant wakes and asks how to proceed.
curl -X POST https://api.agnt.ai/tasks/6702c3a9b8c7d6e5f4a3b2c1/mark-not-done \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]"
# Leave feedback: "like", "dislike", or null to clear. The value is accepted but not yet stored.
curl -X POST https://api.agnt.ai/tasks/6702c3a9b8c7d6e5f4a3b2c1/feedback \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]" \
-H "Content-Type: application/json" \
-d '{ "status": "like" }'
await jane.tasks.stop(task.id);
await jane.tasks.feedback(task.id, 'like');
Schedule a task
Create a task with "status": "scheduled" and a future scheduledFor. It starts on its own at that time.
curl https://api.agnt.ai/tasks \
-X POST \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]" \
-H "Content-Type: application/json" \
-d '{
"title": "Collect three vendor quotes for the lobby HVAC repair",
"assistant": "6702b4c1d2e3f4a5b6c7d8e9",
"status": "scheduled",
"scheduledFor": "2026-11-09T16:00:00.000Z"
}'
Follow-ups inside a task need no scheduling from your app. The assistant sets the task's alarm when it needs to check back, and alarm.headline is the line to show the user.
Workflows
Recurring and event-driven work is a workflow: a skill with kind: "workflow" and one or more triggers. Each firing creates a task.
Trigger on | Fires |
|---|---|
cron | On a fixed schedule. schedule.frequency is minutely, hourly, daily, weekly or monthly. |
webhook | On a calendar or email event, such as event.scheduled, event.cancelled or email.received, immediately or after delayOffset. |
timestamp | Relative to a time on the matched event, such as 30 minutes before startTime. |
condition | When a polled condition becomes true. |
inactivity | When an expected event did not happen within a window. |
system | When another workflow's task completes or fails. |
compound | When several sub-triggers combine with and or or. |
manual | When the user runs it. |
Create a Monday-morning portfolio briefing for Jane:
curl https://api.agnt.ai/skills \
-X POST \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]" \
-H "Content-Type: application/json" \
-d '{
"kind": "workflow",
"name": "weekly-portfolio-briefing",
"title": "Weekly portfolio briefing",
"description": "A Monday-morning summary of leases, maintenance and vendor contracts across the portfolio",
"instructions": "Summarize leases expiring in the next 60 days, open maintenance tickets, and vendor contracts up for renewal.",
"triggers": [{
"on": "cron",
"schedule": { "frequency": "weekly", "days": ["MO"], "hour": 8, "minute": 0 },
"timezone": "America/Los_Angeles"
}],
"maxRuns": 52
}'
| Name | Type | Required | Description |
|---|---|---|---|
kind | string | Yes | workflow. |
name | string | Yes | Unique slug. |
title | string | Yes | Display name. |
description | string | Yes | What the workflow does. |
instructions | string | No | What each run does. |
triggers | object[] | Yes | When the workflow fires. |
scheduleType | string | No | now, recurring or trigger-based. Inferred from triggers when omitted. |
maxRuns, recurrenceEndsAt | integer, string | No | Stop after this many runs, or at this time. |
maxConcurrent, cooldownMs | integer | No | Limit overlapping runs and how often the workflow can fire. |
overflowBehavior | string | No | queue, drop or replace, when a firing arrives at the concurrency limit. |
With X-User-Email, the workflow belongs to that user. With an account key and no X-User-Email, the workflow is available app-wide. Manage skills of every kind with the CLI.
Autonomy
Each user has a supervision matrix (supervisionMatrix): one cell per action type, each holding a mode.
| Mode | Behavior |
|---|---|
auto | Executes immediately. |
time-boxed | Waits timeBoxedWindowMin minutes so the user can review, edit or cancel. When the window ends the cart executes, or is cancelled if timeBoxedExpiry is cancel. While it waits, the cart's status is time_boxed. |
manual | Waits until a person approves or cancels. |
A cell can also set undoDelaySec: how many seconds an action that cannot be reversed (a sent message, a call) waits after it is released, before it goes out. Undo during that hold cancels it. Unset, the hold is 30 seconds. 0 sends at once.
| Action type | Covers | New-user default |
|---|---|---|
email_as_user | Email sent as the user, from their mailbox | manual |
email_as_assistant | Email from the assistant's own address | auto |
messaging | SMS, iMessage, WhatsApp and chat messages | auto |
voice_call_as_assistant | Calls where the assistant speaks as itself | auto |
voice_call_as_user | Calls where the assistant speaks for the user | manual |
calendar | Creating, changing and cancelling events | auto |
drive | Creating, writing and sharing files in a connected drive | auto |
files | Files in the assistant's own storage | auto |
contacts | Changes to the user's contacts | auto |
third_party_skills | Actions through connected apps and MCP servers | auto |
website | Publishing a site | manual |
Reads are never gated. The defaults favor getting work done: only actions taken as the user, and publishing a site, wait for approval. For enterprise use, set email_as_assistant, messaging, voice_call_as_assistant, calendar, drive and third_party_skills to manual or time-boxed, and lock them at the organization so users cannot loosen them.
Read and change a user's settings
curl https://api.agnt.ai/users/6702b1e8f9a0b1c2d3e4f5a6/preferences/supervision/matrix \
-H "Authorization: Bearer $AGNT_API_KEY"
{
"ok": true,
"supervisionMatrix": {
"matrix": {
"email_as_user": { "mode": "manual" },
"email_as_assistant": { "mode": "auto" },
"messaging": { "mode": "auto" }
},
"matrixUpdatedAt": "2026-11-02T16:10:03.000Z",
"parentLockedCells": ["email_as_user"],
"orgLockedCells": ["email_as_user"],
"teamLockedCells": [],
"lockedValues": { "email_as_user": { "mode": "manual" } }
}
}
PATCH merges per cell:
curl https://api.agnt.ai/users/6702b1e8f9a0b1c2d3e4f5a6/preferences/supervision/matrix \
-X PATCH \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "matrix": {
"email_as_assistant": { "mode": "manual" },
"messaging": { "mode": "time-boxed", "timeBoxedWindowMin": 5 }
} }'
| Name | Type | Required | Description |
|---|---|---|---|
matrix.<actionType>.mode | string | No | auto, time-boxed, manual, or null to inherit. |
matrix.<actionType>.timeBoxedWindowMin | number | No | Window length in minutes, for time-boxed. |
matrix.<actionType>.timeBoxedExpiry | string | No | execute or cancel, when the window ends. |
matrix.<actionType>.undoDelaySec | number | No | Hold before an irreversible action goes out. |
A PATCH that would loosen a cell locked by a team or the account returns 403. PUT /users/:userId/preferences/supervision/matrix/reset clears every one of the user's own cells and removes every recipient exception. Each action type then follows the team or organization value if there is one, else the platform default: manual for email_as_user, voice_call_as_user, third_party_skills and website, and auto for the rest.
Recipient exceptions
An exception changes the mode for specific people or apps. Require approval for anything Ava sends to Brightline Freight:
curl https://api.agnt.ai/users/6702b1e8f9a0b1c2d3e4f5a6/preferences/supervision/exceptions \
-X POST \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"label": "Brightline Freight",
"match": { "kind": "domain", "value": "brightlinefreight.com" },
"mode": "manual",
"appliesTo": ["email_as_assistant", "email_as_user", "messaging"]
}'
{
"ok": true,
"recipientException": {
"id": "6702cc48f9a0b1c2d3e4f5a6",
"label": "Brightline Freight",
"match": { "kind": "domain", "value": "brightlinefreight.com" },
"mode": "manual",
"timeBoxedWindowMin": null,
"timeBoxedExpiry": null,
"undoDelaySec": null,
"appliesTo": ["email_as_assistant", "email_as_user", "messaging"],
"createdAt": "2026-11-02T16:12:40.000Z"
}
}
| Name | Type | Required | Description |
|---|---|---|---|
match.kind | string | Yes | email, domain, contactId, company, tag, priority ("1" to "3"), internal ("true" or "false") or connection (a connected app, by skill name). |
match.value | string | Yes | The value to match. |
mode | string | Yes | auto, time-boxed, manual, or denied to block the action. |
appliesTo | string[] | No | Action types it covers. Empty, or ["All"], covers every action type with a recipient. |
label | string | No | Up to 60 characters. |
timeBoxedWindowMin | integer | No | 1 to 60. |
undoDelaySec | integer | No | 0 to 300. |
A loosening exception applies only when every recipient of the action is covered by one. Each user can have up to 100 exceptions. Remove one with DELETE /users/:userId/preferences/supervision/exceptions/:exceptionId.
Per-task override
A person can set the mode for a single task, for every kind of action on it:
curl https://api.agnt.ai/tasks/6702c3a9b8c7d6e5f4a3b2c1 \
-X PUT \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]" \
-H "Content-Type: application/json" \
-d '{ "approvalOverride": { "mode": "manual" } }'
Set approvalOverride to null to inherit again. The assistant can tighten a task's mode on its own, never loosen it. An admin lock outranks the override.
Organization and team policy
Admins set autonomy cells for an organization or team and can lock them. A locked cell is pinned to the admin's value: the user's own setting, exceptions and task overrides cannot change it. An unlocked admin value is only a default, which the user's own cell replaces.
curl https://api.agnt.ai/organizations/6702a9c3e1b4f5d6a7b8c901/settings/resource-permissions \
-X PATCH \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "matrix": {
"email_as_user": { "mode": "manual" },
"email_as_assistant": { "mode": "manual" },
"locked": ["email_as_user", "email_as_assistant"]
} }'
For a team, send { "approvalSettings": { "matrix": { } } } with the same cells to PATCH /teams/:teamId/settings/resource-permissions. Team admins can set and lock only cells the organization has not locked. Where several locks apply, the most conservative wins. Users, organizations and teams covers the rest of organization and team policy.
The mode for an action is decided in this order:
- A
deniedexception blocks the action. - An admin lock decides.
- The task's
approvalOverride. - The most conservative matching exception.
- The user's own cell.
- The platform default.
The task reports the result in effectiveApprovalMode and any admin locks in approvalLocks.
Approvals
When an action needs review, the task stages it in a cart and waits. A cart's id is the id of the task that staged it. The approval endpoints live under /portal on api.agnt.ai and take the same API key and X-User-Email as every other call.
| Cart status | Description |
|---|---|
open | The run is still staging actions. Nothing is submitted for review. |
submitted | Staging finished and the approval mode is being resolved. Moves on within moments. |
pending_approval | Waiting for a person (manual). |
time_boxed | Executes at timeBoxedExpiresAt unless edited or cancelled. |
approved | A person approved it, or its window ended. Execution is about to start. |
executing | The stored actions are being sent. |
executed | Every action ran. |
cancelled | Cancelled with POST /portal/carts/:cartId/cancel, or its window ended with timeBoxedExpiry: "cancel". Nothing was sent. |
rejected | Declined from an approval link, such as the Reject link in an approval email. Nothing was sent. |
failed | Execution failed. The task's activities show which actions, if any, went out. |
Every approval has these guarantees:
- Exactly what was shown. Every cart response carries a
contentHashof its staged content. Approve must echo it. If the content changed since it was shown, approve returns409 cart_content_changedand nothing is sent. - No model in between. Approval executes the stored actions. No model call runs between approval and execution, so nothing is rewritten after review.
- A person approves. Approve, edit, cancel and undo accept a person's own session or an API key acting as that person. Certificate JWTs, the SDK's credential, get
403, so the SDK has no approval methods. - Edits ship verbatim. Editing a staged email's subject or body changes the content hash, and the edited text is what goes out.
Review and approve
List the carts waiting for the user, then read one for its staged actions and contentHash:
curl https://api.agnt.ai/portal/carts \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]"
curl https://api.agnt.ai/portal/carts/6702c3a9b8c7d6e5f4a3b2c1 \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]"
{
"ok": true,
"cart": {
"id": "6702c3a9b8c7d6e5f4a3b2c1",
"taskId": "6702c3a9b8c7d6e5f4a3b2c1",
"taskTitle": "Renew the Brightline Freight service contract",
"assistantName": "Ava",
"status": "pending_approval",
"resolvedApprovalMode": "manual",
"timeBoxedExpiresAt": null,
"timeBoxedExpiry": "execute",
"dispatches": [
{
"index": 0,
"type": "send_message_as_assistant",
"platform": "email",
"from": "[email protected]",
"to": ["[email protected]"],
"cc": ["[email protected]"],
"bcc": [],
"subject": "Harborview service agreement renewal, 2027 terms",
"body": "Hi Dana,\n\nOur service agreement with Brightline Freight ends on January 1, 2027. Harborview would like to renew. Could you send your proposed 2027 terms and rates by November 20? The current agreement is attached for reference.\n\nBest,\nAva, on behalf of Jane Rivera",
"description": "Email Brightline Freight legal asking for 2027 renewal terms",
"payload": null,
"files": [
{
"filename": "Brightline-MSA-2026.pdf",
"mimeType": "application/pdf",
"size": 482113,
"fileId": "6702c804b5c6d7e8f9a0b1c2"
}
],
"editedByUser": false,
"currentVersionId": null,
"validUntil": null,
"evergreen": false
}
],
"contentHash": "cv1:3b8f1c2d9e4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c",
"createdAt": "2026-11-02T16:25:02.000Z",
"updatedAt": "2026-11-02T16:25:04.000Z"
}
}
Approve exactly that content:
curl https://api.agnt.ai/portal/carts/6702c3a9b8c7d6e5f4a3b2c1/approve \
-X POST \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]" \
-H "Content-Type: application/json" \
-d '{ "contentHash": "cv1:3b8f1c2d9e4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c" }'
{ "ok": true, "cartId": "6702c3a9b8c7d6e5f4a3b2c1", "status": "approved" }
| Name | Type | Required | Description |
|---|---|---|---|
contentHash | string | Yes | The contentHash from the read you showed the user. |
reason | string | No | A note recorded with the approval. |
Call approve only when the person has chosen to approve in your UI. Never approve from your backend on their behalf.
| Error | Status | Meaning |
|---|---|---|
content_hash_required | 400 | contentHash is missing. |
cart_already_resolved | 400 | The cart is no longer awaiting approval. |
cart_task_closed | 400 | The task closed and its draft was withdrawn. |
cart_content_changed | 409 | The content changed since it was read. Read it again and show the user. |
Edit, remove or cancel
# Edit a staged message's subject or body. The contentHash changes.
curl https://api.agnt.ai/portal/carts/6702c3a9b8c7d6e5f4a3b2c1/dispatches/0/edit \
-X PATCH \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]" \
-H "Content-Type: application/json" \
-d '{ "subject": "Harborview service agreement renewal, 2027 terms (reply by Nov 20)" }'
# Remove one action from the cart.
curl -X DELETE https://api.agnt.ai/portal/carts/6702c3a9b8c7d6e5f4a3b2c1/dispatches/0 \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]"
# Cancel the whole cart. The task is told and decides what to do next.
curl https://api.agnt.ai/portal/carts/6702c3a9b8c7d6e5f4a3b2c1/cancel \
-X POST \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]" \
-H "Content-Type: application/json" \
-d '{ "reason": "Wait until the Q1 2027 board meeting prep is done" }'
The edit endpoint returns the updated cart with its new contentHash. It accepts subject, body, validUntil and evergreen. Recipients cannot be changed there: to, cc, bcc or payload return 400. Edits are accepted only while the cart is pending_approval or time_boxed, and editing a time_boxed cart extends its countdown.
Undo
# What can still be undone, and until when.
curl https://api.agnt.ai/portal/carts/6702c3a9b8c7d6e5f4a3b2c1/snapshots \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]"
{
"cartId": "6702c3a9b8c7d6e5f4a3b2c1",
"count": 1,
"snapshots": [
{
"dispatchIndex": 0,
"dispatchType": "send_message_as_assistant",
"resourceType": "email",
"reversible": false,
"undoStatus": "available",
"undoExpiresAt": "2026-11-02T16:31:30.000Z",
"undoneAt": null,
"undoAvailable": true
}
]
}
# Undo one action.
curl -X POST https://api.agnt.ai/portal/carts/6702c3a9b8c7d6e5f4a3b2c1/dispatches/0/undo \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]"
# Undo every reversible action, in reverse order.
curl -X POST https://api.agnt.ai/portal/carts/6702c3a9b8c7d6e5f4a3b2c1/undo \
-H "Authorization: Bearer $AGNT_API_KEY" \
-H "X-User-Email: [email protected]"
A reversible action, such as a created calendar event, is reverted. An irreversible action, such as a sent email, can be cancelled only while it is inside its undoDelaySec hold. After undoExpiresAt, undo returns an error.
Tasks the assistant proposes
An assistant can propose a task instead of starting it. A proposed task has createdBy: "agent" and approvalGate: "pending_user_approval", and does not run until approved. List proposed tasks with GET /tasks?approvalGate=pending_user_approval. Approve one with POST /tasks/:taskId/approve, decline it with POST /tasks/:taskId/decline (optional { "reason" }), or approve up to 100 with POST /tasks/approve-batch and { "taskIds": [] }.
Kill switch
The account-wide kill switch freezes every outbound action for every user at once. While frozen, staged actions are refused, task creation and runs return 423 with error_code: "KILL_SWITCH_ENGAGED", and inbound messages are not processed.
A person signed in to your hosted app freezes and releases the account. The console, API keys, certificate JWTs and service tokens get 403 (KILL_SWITCH_RELEASE_FORBIDDEN), so a leaked credential cannot release a freeze. Your backend can read the state:
curl https://api.agnt.ai/portal/account/kill-switch \
-H "Authorization: Bearer $AGNT_API_KEY"
{ "state": "active", "engagedAt": null, "engagedBy": null, "reason": null }
state is frozen while engaged and active otherwise. To stop a single task, use POST /tasks/:taskId/stop.
Example: renew the Brightline Freight contract
Harborview's service agreement with Brightline Freight ends on 2027-01-01. Jane asks Ava to get the 2027 terms. Harborview requires approval for every email Ava sends.
-
Require approval for assistant email.
curl https://api.agnt.ai/users/6702b1e8f9a0b1c2d3e4f5a6/preferences/supervision/matrix \ -X PATCH \ -H "Authorization: Bearer $AGNT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "matrix": { "email_as_assistant": { "mode": "manual" } } }' -
Create and start the task. Create it as in Create and run a task, then start it with the MSA attached. The task id is
6702c3a9b8c7d6e5f4a3b2c1. -
Ava stages the email. Ava reads
Brightline-MSA-2026.pdf, drafts an email to [email protected], and stages it. The task shows the cart:curl https://api.agnt.ai/tasks/6702c3a9b8c7d6e5f4a3b2c1 \ -H "Authorization: Bearer $AGNT_API_KEY" \ -H "X-User-Email: [email protected]"{ "ok": true, "task": { "id": "6702c3a9b8c7d6e5f4a3b2c1", "title": "Renew the Brightline Freight service contract", "status": "executing", "effectiveApprovalMode": "manual", "pendingCart": { "status": "pending_approval", "label": "Email Brightline Freight legal for 2027 renewal terms", "timeBoxedExpiresAt": null, "timeBoxedExpiry": null, "dispatches": [ { "type": "send_message_as_assistant", "platform": "email", "to": ["[email protected]"], "subject": "Harborview service agreement renewal, 2027 terms", "resolvedApprovalMode": "manual" } ] } } } -
Jane approves it in your app. Your app reads the cart, shows Jane the email and the attachment, and when she taps Approve, sends the
contentHashit showed her (see Review and approve):curl https://api.agnt.ai/portal/carts/6702c3a9b8c7d6e5f4a3b2c1/approve \ -X POST \ -H "Authorization: Bearer $AGNT_API_KEY" \ -H "X-User-Email: [email protected]" \ -H "Content-Type: application/json" \ -d '{ "contentHash": "cv1:3b8f1c2d9e4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c" }' -
The email goes out and the task waits.
statusbecomesawaiting_non_user_response,waitingOnContactslists Dana Okafor, andalarm.wakeAtis set so Ava follows up if Brightline does not reply. -
Dana replies with the 2027 terms. The reply is attached to the task and wakes it. Your app makes no call.
-
The task resumes. Ava compares the proposed terms with the 2026 agreement and asks Jane to confirm a 3% rate increase.
statusbecomesawaiting_user_input. Jane answers withPOST /tasks/6702c3a9b8c7d6e5f4a3b2c1/process, and the task continues.
Follow every step with GET /tasks/6702c3a9b8c7d6e5f4a3b2c1/activities.
Endpoints
| Method | Path | Description |
|---|---|---|
POST | /tasks | Create a task. |
GET | /tasks | List tasks. Filter by status, type, assistant, approvalGate or q. |
GET | /tasks/:taskId | Get a task. |
PUT | /tasks/:taskId | Update a task, including approvalOverride. |
DELETE | /tasks/:taskId | Delete a task. |
POST | /tasks/:taskId/process | Start a run, or send the task a message. |
GET | /tasks/:taskId/activities | The timeline, newest first. |
POST | /tasks/:taskId/stop | Stop the current run. Status becomes on_hold. |
POST | /tasks/:taskId/mark-done | Mark done. |
POST | /tasks/:taskId/mark-not-done | Reopen a done task. |
POST | /tasks/:taskId/feedback | Like, dislike or clear. The value is accepted but not yet stored. |
POST | /tasks/:taskId/approve | Approve a proposed task. |
POST | /tasks/:taskId/decline | Decline a proposed task. |
POST | /tasks/approve-batch | Approve several proposed tasks. |
POST | /skills | Create a workflow (kind: "workflow"). |
GET | /users/:userId/preferences/supervision/matrix | Read a user's autonomy settings and locks. |
PATCH | /users/:userId/preferences/supervision/matrix | Change cells. |
PUT | /users/:userId/preferences/supervision/matrix/reset | Clear the user's cells and exceptions. |
POST | /users/:userId/preferences/supervision/exceptions | Add a recipient exception. |
DELETE | /users/:userId/preferences/supervision/exceptions/:exceptionId | Remove a recipient exception. |
PATCH | /organizations/:orgId/settings/resource-permissions | Organization cells and locks. |
PATCH | /teams/:teamId/settings/resource-permissions | Team cells and locks. |
GET | /portal/carts | Carts awaiting review. |
GET | /portal/carts/:cartId | One cart and its contentHash. |
PATCH | /portal/carts/:cartId/dispatches/:index/edit | Edit a staged action's text. |
DELETE | /portal/carts/:cartId/dispatches/:index | Remove a staged action. |
POST | /portal/carts/:cartId/approve | Approve. |
POST | /portal/carts/:cartId/cancel | Cancel. |
GET | /portal/carts/:cartId/snapshots | What can be undone. |
POST | /portal/carts/:cartId/dispatches/:index/undo | Undo one action. |
POST | /portal/carts/:cartId/undo | Undo every reversible action. |
GET | /portal/account/kill-switch | Read the kill switch state. |
The SDK covers tasks.create, list, get, update, delete, process, stop, feedback and updateAssignees. Autonomy settings, carts and the kill switch are REST only.
Limits
GET /portal/cartsreturns at most 50 carts and is not paginated. Filter withstatus=pending_approval,time_boxed.GET /taskspages withpageandperPage. An account key withoutX-User-Emaillists every user's tasks and narrows them withownerEmail.modelTiercan be changed only before a task's first run.- Exception
timeBoxedWindowMinis 1 to 60 minutes, andundoDelaySecis 0 to 300 seconds. - Followers of a shared task can read its cart. Only the owner can approve, edit, cancel or undo it.
See also
- Prime: the harness that runs every task
- Authentication: API keys,
X-User-Emailand certificate JWTs - Users, organizations and teams: organization and team policy
- Security: how approvals, the kill switch and isolation fit together