client.Tasks
Every method in this namespace: its signature, its parameters, what it returns and an example.
Methods
Instructions the assistant runs by itself, on a schedule, when mail arrives or when asked: create a task, change, pause, resume and delete it, run it now, hand the assistant a one-off job, follow each run step by step, and approve or deny what a run asks before it acts.
Tasks.ListTasks.ListAllTasks.IterateTasks.GetTasks.CreateTasks.UpdateTasks.DeleteTasks.PauseTasks.ResumeTasks.RunTasks.ListRunsTasks.ListAllRunsTasks.IterateRunsTasks.GetRunTasks.StartRunTasks.CancelRunTasks.ListApprovalsTasks.ListAllApprovalsTasks.IterateApprovalsTasks.GetApprovalTasks.ApproveTasks.Deny
Tasks.List
List the tasks of the workspace, a page at a time
List(ctx context.Context, opts ...openemail.RequestOption) (*openemail.Page, error)Returns one page of tasks, the most recently changed first. ListAll collects every page and Iterate walks them lazily. A task is an instruction in plain words that the assistant runs by itself: on a schedule, when mail arrives, or only when asked. Each one says what starts it in trigger, how much it may do without asking in autonomy, when it runs next in nextRunAt, how its last run ended in lastRunStatus and how many approval requests wait for an answer in pendingApprovals.
Give openemail.WithStatus to read only the tasks that are active or only the ones that are paused. pausedReason says why a paused task stopped: user when somebody paused it, failures after 5 failed runs in a row, and plan_limit when the plan no longer covers it.
Parameters
openemail.WithStatus...stringOnly tasks in this state:
activeorpaused. Left out, both are listed.openemail.WithLimitintPage size, from 1 to 100. The server defaults to 25.
openemail.WithCursorstringThe
NextCursorof the previous page, passed back as it came. Leave it out for the first page.openemail.WithAPIKeystringOverrides the client API key for this call only.
Returns
A *openemail.Page with Items, HasMore and NextCursor. Each item has id, name, instruction, status, pausedReason, trigger, autonomy, toolkits, model, notify, maxRunsPerDay, maxSteps, nextRunAt, heldUntil, lastRunAt, lastRunStatus, runCount, pendingApprovals, chatId, createdAt and updatedAt.
Example
page, err := client.Tasks.List(ctx, openemail.WithStatus("active"))if err != nil { return err} for _, task := range page.Items { fmt.Println(task.String("name"), task.Object("trigger").String("kind"), task.String("nextRunAt"), task.Int("pendingApprovals"))} fmt.Println(page.HasMore, page.NextCursor)Notes
Reading tasks needs
threads:readandemails:readbesidetasks:read.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 a member whose role does not reach every address with 403owner_only.The order follows
updatedAt, which moves when a task changes or starts a run. A workspace holds at most 100 tasks, soopenemail.WithLimit(100)reads them all in one request and no task can move between pages.
Also available in
- API
GET /tasks- TypeScript
tasks.list()- Python
tasks.list()- Ruby
tasks.list- PHP
tasks->list- Java
tasks().list- C#
Tasks.ListAsync- CLI
openemail tasks list
Tasks.ListAll
Collect every task into one slice
ListAll(ctx context.Context, opts ...openemail.RequestOption) ([]openemail.Object, error)Walks every page of List and returns with all tasks, the most recently changed first. One request per page. Give openemail.WithStatus to collect only the active tasks or only the paused ones.
Parameters
openemail.WithStatus...stringOnly tasks in this state:
activeorpaused. Left out, both are collected.openemail.WithLimitintPage size for each request, from 1 to 100. The server defaults to 25.
openemail.WithCursorstringStarts the walk after this cursor instead of the first page.
openemail.WithAPIKeystringOverrides the client API key for every page of this walk.
Returns
A []openemail.Object holding every task.
Example
tasks, err := client.Tasks.ListAll(ctx, openemail.WithLimit(100))if err != nil { return err} for _, task := range tasks { if task.Int("pendingApprovals") > 0 { fmt.Println(task.String("name"), task.Int("pendingApprovals")) }}Notes
If any page fails the call fails and the tasks already fetched are discarded.
A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.
Also available in
- API
GET /tasks- TypeScript
tasks.listAll()- Python
tasks.list_all()- Ruby
tasks.list_all- PHP
tasks->listAll- Java
tasks().listAll- C#
Tasks.ListAllAsync
Tasks.Iterate
Stream the tasks one at a time
Iterate(ctx context.Context, opts ...openemail.RequestOption) *openemail.IteratorReturns an iterator that yields tasks individually, the most recently changed first, and requests the next page only once the current one is drained. Nothing is fetched until you consume it, and breaking out of the loop stops the requests.
Parameters
openemail.WithStatus...stringOnly tasks in this state:
activeorpaused. Left out, both are walked.openemail.WithLimitintPage size for each request, from 1 to 100. The server defaults to 25.
openemail.WithCursorstringStarts the walk after this cursor instead of the first page.
openemail.WithAPIKeystringOverrides the client API key for every page of this walk.
Returns
An *openemail.Iterator yielding one task per step.
Example
for task, err := range client.Tasks.Iterate(ctx, openemail.WithStatus("paused")).All() { if err != nil { return err } fmt.Println(task.String("name"), "was paused by", task.String("pausedReason"))}Notes
The iterator is lazy, so an abandoned loop costs only the pages you consumed.
A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.
Also available in
- API
GET /tasks- TypeScript
tasks.iterate()- Python
tasks.iterate()- Ruby
tasks.iterate- PHP
tasks->iterate- Java
tasks().iterate- C#
Tasks.IterateAsync
Tasks.Get
Read one task by id
Get(ctx context.Context, id string, opts ...openemail.RequestOption) (openemail.Object, error)Looks up a single task and returns it in the same shape as a row of List: its instruction, its trigger with the schedule or the mail filter, how much it may do alone, its toolkits and limits, when it runs next and how the last run ended.
nextRunAt is set for an active schedule task and null for the other kinds and while paused. heldUntil is set when the workspace ran out of AI actions for the day: the task starts nothing until then. pendingApprovals counts the approval requests of its runs that still wait for an answer.
Parameters
idstringRequiredTask id such as
tsk_5c1e9a7b3d2f48e6a0b4c7d1, asListreturns it.openemail.WithAPIKeystringOverrides the client API key for this call only.
Returns
An openemail.Object with id, name, instruction, status, pausedReason, trigger, autonomy, toolkits, model, notify, maxRunsPerDay, maxSteps, nextRunAt, heldUntil, lastRunAt, lastRunStatus, runCount, pendingApprovals, chatId, createdAt and updatedAt.
Example
task, err := client.Tasks.Get(ctx, "tsk_5c1e9a7b3d2f48e6a0b4c7d1")if err != nil { return err} fmt.Println(task.String("name"), task.String("status"), task.Object("trigger").String("kind"), task.String("nextRunAt"))Notes
A missing task fails with an
*openemail.Errorthat matchesopenemail.ErrNotFoundand codetask_not_found.A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.
Also available in
- API
GET /tasks/{id}- TypeScript
tasks.get()- Python
tasks.get()- Ruby
tasks.get- PHP
tasks->get- Java
tasks().get- C#
Tasks.GetAsync- CLI
openemail tasks get
Tasks.Create
Save a task for the assistant to run by itself
Create(ctx context.Context, body openemail.Body, opts ...openemail.RequestOption) (openemail.Object, error)Saves an instruction for the assistant with what starts it and how much it may do alone, and returns the task as it is stored. instruction says what to do in plain words, as you would tell a person. It is the only instruction a run follows: text inside emails is read, never obeyed.
trigger.kind says what starts a run. A schedule task needs trigger.schedule and gets its first run time at once, in nextRunAt. A mail task starts with the next message that reaches an inbox and matches trigger.mail, or with every message when no filter is sent. Mail from an address of the workspace itself never starts one. A manual task waits for Run.
autonomy limits what a run does without asking. ask sends every change to a person to approve. organize, the default, lets the run label, archive, bin, snooze, draft, take notes and keep contacts by itself, and email the addresses of the workspace. reply also lets it answer an email without asking, as long as it adds no recipients. Anything else waits as an approval request, which ListApprovals reads. A run never reaches billing, members, roles, domains, rules, keys or webhooks.
A task acts with what its caller holds, so the call needs more than tasks:write: threads:read, threads:write, emails:read, emails:send and labels:write, and the read and write scopes of every toolkit the task names.
Parameters
namestringRequiredWhat the task is called in the app, trimmed, 1 to 120 characters.
instructionstringRequiredWhat the assistant should do, in plain words, trimmed, 1 to 8,000 characters.
trigger.kindstringRequiredscheduleruns on a clock,mailruns when a message arrives, andmanualruns only when started withRun.trigger.schedule.cadencestringNeeded for a
scheduletask and ignored for the other kinds.hourlyruns everyeveryHourshours atminute,dailyruns athour:minuteon each ofweekdays, andmonthlyruns athour:minuteonmonthDay.trigger.schedule.everyHoursintFor
hourly: how many hours lie between runs, from 1 to 12, counted from midnight in the time zone. Defaults to 1.trigger.schedule.hourintThe hour of the day, 0 to 23, in the time zone of the schedule. Defaults to 8.
trigger.schedule.minuteintThe minute of the hour, 0 to 59. Defaults to 0.
trigger.schedule.weekdays[]intFor
daily: the days it runs, 0 for Sunday to 6 for Saturday. Left out, it runs every day.trigger.schedule.monthDayintFor
monthly: the day of the month, 1 to 28. Defaults to 1.trigger.schedule.timezonestringAn IANA time zone such as
Europe/Berlin. The schedule follows its clock changes. Defaults toUTC.trigger.mail.addresses[]stringFor a
mailtask: start only for mail delivered to one of these addresses of the workspace, at most 25. Left out or empty, every address counts.trigger.mail.fromContainsstring | nilFor a
mailtask: start only when the sender address contains this text, whatever the case, up to 200 characters.trigger.mail.subjectContainsstring | nilFor a
mailtask: start only when the subject contains this text, whatever the case, up to 200 characters.autonomystringHow much a run may do without asking:
ask,organizeorreply. Defaults toorganize.toolkits[]stringExtra groups of tools a run may use beyond reading and organising mail:
contacts,audiences,templates,tracking,translate,files,notes,drafts,sent,insights,subscriptions,calendar,knowledgeandweb. Left out,drafts,notesandknowledge. An empty slice gives it none.modelstringstandardis the everyday model.advancedreasons better over long jobs and costs ten AI actions where the standard one costs one. Defaults tostandard.notifystringattentionnotifies the workspace owner when a run waits for approval or fails,alwaysalso when it finishes, andneverstays silent. Defaults toattention.maxRunsPerDayintThe most runs the task may start in any 24 hours, however often its trigger fires, from 1 to 500. Defaults to 50.
maxStepsintThe most model steps one run may take before it has to write its report, from 1 to 60. Defaults to 20.
pausedbooltruecreates the task paused, so it does not run untilResume. A paused task does not count against the active tasks of the plan.openemail.WithAPIKeystringOverrides the client API key for this call only.
Returns
An openemail.Object with the new id, status active, or paused when you asked for that, the trigger as it is stored with every default filled in, and nextRunAt set for a schedule.
Example
task, err := client.Tasks.Create(ctx, openemail.Body{ "name": "Label invoices", "instruction": "When an invoice arrives, label it Invoices and add a note with the amount and the due date.", "trigger": openemail.Body{"kind": "mail", "mail": openemail.Body{"subjectContains": "invoice"}}, "autonomy": "organize",})if err != nil { return err} fmt.Println(task.String("id"), task.String("status"), task.Object("trigger").String("kind"))Notes
Each toolkit brings its own scopes:
contactsneedscontacts:readandcontacts:write,audiencesneedsaudiences:read,audiences:writeandcontacts:read, andtemplates,files,drafts,calendarandknowledgeneed their read and write scope. The default toolkits therefore needdrafts:read,drafts:write,knowledge:readandknowledge:write. A missing scope is a 403insufficient_scope.A workspace runs 1 active task on Free, 5 on Starter and 25 on Business, with no cap on Enterprise, and holds at most 100 tasks, paused ones included. One more active task than the plan covers is a 403
task_active_limit_reached, and a task past the 100 a 403task_limit_reached.A
scheduletrigger without its schedule, a time zone that is not known and a toolkit a task may not use are 422invalid_task. A value of the wrong type or length is 422invalid_parameterwithparamnaming it, and a key the body does not take is 422unknown_parameter.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 a member whose role does not reach every address with 403owner_only.Not retried automatically, and nothing deduplicates by name, so a retry after a lost response can leave two tasks. List them and delete the spare.
Also available in
- API
POST /tasks- TypeScript
tasks.create()- Python
tasks.create()- Ruby
tasks.create- PHP
tasks->create- Java
tasks().create- C#
Tasks.CreateAsync- CLI
openemail tasks create
Tasks.Update
Change what a task does, what starts it or how much it may do alone
Update(ctx context.Context, id string, patch openemail.Body, opts ...openemail.RequestOption) (openemail.Object, error)Changes a task and returns it as it is stored. Send only what changes: a field left out stays as it is, and a patch with nothing in it is a 422.
A new trigger replaces the old one whole. Send its kind and, for a schedule, the whole schedule again, because what the new trigger leaves out goes back to its default, not to what the task had. A schedule change moves nextRunAt of an active task.
Runs already started keep the settings they began with, and later runs follow the new ones. It neither pauses nor resumes a task: Pause and Resume do that. Like Create, the call needs the scopes the task acts with, counted from the toolkits it has after the change.
Parameters
idstringRequiredTask id such as
tsk_5c1e9a7b3d2f48e6a0b4c7d1, asListreturns it.namestringNew name, trimmed, 1 to 120 characters. Left out, the name stays.
instructionstringNew instruction in plain words, trimmed, 1 to 8,000 characters. Left out, the instruction stays.
trigger.kindstringNeeded whenever any part of
triggeris sent:schedule,mailormanual.trigger.schedule.cadencestringNeeded for a
scheduletrigger:hourly,dailyormonthly.trigger.schedule.everyHoursintFor
hourly: how many hours lie between runs, from 1 to 12, counted from midnight in the time zone. Defaults to 1.trigger.schedule.hourintThe hour of the day, 0 to 23, in the time zone of the schedule. Defaults to 8.
trigger.schedule.minuteintThe minute of the hour, 0 to 59. Defaults to 0.
trigger.schedule.weekdays[]intFor
daily: the days it runs, 0 for Sunday to 6 for Saturday. Left out, it runs every day.trigger.schedule.monthDayintFor
monthly: the day of the month, 1 to 28. Defaults to 1.trigger.schedule.timezonestringAn IANA time zone such as
Europe/Berlin. Defaults toUTC.trigger.mail.addresses[]stringFor a
mailtrigger: start only for mail delivered to one of these addresses of the workspace, at most 25. Left out or empty, every address counts.trigger.mail.fromContainsstring | nilFor a
mailtrigger: start only when the sender address contains this text, whatever the case, up to 200 characters.trigger.mail.subjectContainsstring | nilFor a
mailtrigger: start only when the subject contains this text, whatever the case, up to 200 characters.autonomystringHow much a run may do without asking:
ask,organizeorreply. Left out, it stays.toolkits[]stringThe toolkits a run may use from now on, replacing the list the task had. An empty slice gives it none. Left out, they stay.
modelstringstandardoradvanced, which costs ten AI actions where the standard one costs one. Left out, it stays.notifystringWhen the workspace owner is notified:
attention,alwaysornever. Left out, it stays.maxRunsPerDayintThe most runs the task may start in any 24 hours, from 1 to 500. Left out, it stays.
maxStepsintThe most model steps one run may take, from 1 to 60. Left out, it stays.
openemail.WithAPIKeystringOverrides the client API key for this call only.
Returns
An openemail.Object with the unchanged id and status, and everything else as it is now.
Example
task, err := client.Tasks.Update(ctx, "tsk_5c1e9a7b3d2f48e6a0b4c7d1", openemail.Body{ "trigger": openemail.Body{ "kind": "schedule", "schedule": openemail.Body{ "cadence": "daily", "hour": 8, "weekdays": []int{1, 2, 3, 4, 5}, "timezone": "Europe/Berlin", }, },})if err != nil { return err} fmt.Println(task.Object("trigger").Object("schedule"), task.String("nextRunAt"))Notes
A missing task is a 404
task_not_found. An empty patch or a value of the wrong type or length is 422invalid_parameter, a key the patch does not take 422unknown_parameter, and a trigger without its schedule, a time zone that is not known or a toolkit a task may not use 422invalid_task.The key or app needs the read and write scopes of every toolkit the task has after the change, or the call is a 403
insufficient_scope.A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.The SDK retries this call after a network failure, since the same patch sent twice leaves the same task.
Also available in
- API
PATCH /tasks/{id}- TypeScript
tasks.update()- Python
tasks.update()- Ruby
tasks.update- PHP
tasks->update- Java
tasks().update- C#
Tasks.UpdateAsync- CLI
openemail tasks update
Tasks.Delete
Delete a task with every run it made
Delete(ctx context.Context, id string, opts ...openemail.RequestOption) (openemail.Object, error)Deletes a task for good, with every run it made and their records. A run in flight is stopped and its open approval requests are closed. What the runs already did in the mailbox stays done.
There is no undo. To stop a task and keep its history, use Pause.
Parameters
idstringRequiredTask id such as
tsk_5c1e9a7b3d2f48e6a0b4c7d1, asListreturns it.openemail.WithAPIKeystringOverrides the client API key for this call only.
Returns
An openemail.Object with object set to task, id and deleted set to true.
Example
removed, err := client.Tasks.Delete(ctx, "tsk_5c1e9a7b3d2f48e6a0b4c7d1")if err != nil { return err} fmt.Println(removed.String("id"), removed.Bool("deleted"))Notes
The SDK does not retry a delete. A 404
task_not_foundon your own second attempt after a lost response means the first one worked.A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.
Also available in
- API
DELETE /tasks/{id}- TypeScript
tasks.delete()- Python
tasks.delete()- Ruby
tasks.delete- PHP
tasks->delete- Java
tasks().delete- C#
Tasks.DeleteAsync- CLI
openemail tasks delete
Tasks.Pause
Stop a task from starting new runs
Pause(ctx context.Context, id string, opts ...openemail.RequestOption) (openemail.Object, error)Stops a task from starting new runs: its schedule no longer fires and arriving mail no longer starts it. A run already going carries on, and its approval requests can still be answered. Run still starts a paused task by hand. Pausing a paused task leaves it paused. It takes no body.
Parameters
idstringRequiredTask id such as
tsk_5c1e9a7b3d2f48e6a0b4c7d1, asListreturns it.openemail.WithAPIKeystringOverrides the client API key for this call only.
Returns
An openemail.Object with status paused, pausedReason user and nextRunAt null.
Example
task, err := client.Tasks.Pause(ctx, "tsk_5c1e9a7b3d2f48e6a0b4c7d1")if err != nil { return err} fmt.Println(task.String("status"), task.String("pausedReason"))Notes
A missing task is a 404
task_not_found.To stop a run that is already going, use
CancelRun.A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.Retried automatically on network failure and retryable statuses, since pausing twice leaves the same paused task.
Also available in
- API
POST /tasks/{id}/pause- TypeScript
tasks.pause()- Python
tasks.pause()- Ruby
tasks.pause- PHP
tasks->pause- Java
tasks().pause- C#
Tasks.PauseAsync- CLI
openemail tasks pause
Tasks.Resume
Turn a paused task back on
Resume(ctx context.Context, id string, opts ...openemail.RequestOption) (openemail.Object, error)Turns a paused task back on, whoever or whatever paused it: a person, 5 failed runs in a row or the plan. It clears the failure count and heldUntil, and gives a schedule task its next run time. A schedule picks up at its next time, and the runs it missed while paused are not made up. It takes no body.
A task acts with what its caller holds, so the call needs the same scopes as Create: threads:read, threads:write, emails:read, emails:send and labels:write beside tasks:write, and the read and write scopes of every toolkit the task names.
Parameters
idstringRequiredTask id such as
tsk_5c1e9a7b3d2f48e6a0b4c7d1, asListreturns it.openemail.WithAPIKeystringOverrides the client API key for this call only.
Returns
An openemail.Object with status active, pausedReason and heldUntil null and, for a schedule, nextRunAt set.
Example
task, err := client.Tasks.Resume(ctx, "tsk_5c1e9a7b3d2f48e6a0b4c7d1")if err != nil { return err} fmt.Println(task.String("status"), task.String("nextRunAt"))Notes
A plan that covers no more active tasks answers 403
task_active_limit_reached: 1 on Free, 5 on Starter, 25 on Business and no cap on Enterprise. A missing task is a 404task_not_found.A key or an app without the scopes of a toolkit the task names is refused with 403
insufficient_scope.A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.Retried automatically on network failure and retryable statuses, since resuming twice leaves the same active task.
Also available in
- API
POST /tasks/{id}/resume- TypeScript
tasks.resume()- Python
tasks.resume()- Ruby
tasks.resume- PHP
tasks->resume- Java
tasks().resume- C#
Tasks.ResumeAsync- CLI
openemail tasks resume
Tasks.Run
Start a run of a task now
Run(ctx context.Context, id string, opts ...openemail.RequestOption) (openemail.Object, error)Starts a run of a task at once, whatever its trigger and whether or not it is paused, and returns the run as it was queued. The run works in the background. Read it again with GetRun to follow it until status is done, failed or cancelled. While it is waiting, an approval request is open, which ListApprovals reads.
The run uses the instruction and the settings the task has now, and counts towards its maxRunsPerDay. It takes no body. A task acts with what its caller holds, so the call needs the same scopes as Create, counted from the toolkits of the task.
Parameters
idstringRequiredTask id such as
tsk_5c1e9a7b3d2f48e6a0b4c7d1, asListreturns it.openemail.WithAPIKeystringOverrides the client API key for this call only.
Returns
An openemail.Object with a new id, status queued, trigger manual and the taskId and taskName of the task. transcript and approvals fill in as the run works.
Example
queued, err := client.Tasks.Run(ctx, "tsk_5c1e9a7b3d2f48e6a0b4c7d1")if err != nil { return err} run, err := client.Tasks.GetRun(ctx, queued.ID())if err != nil { return err} fmt.Println(run.String("status"), run.String("result"))Notes
A task that has started
maxRunsPerDayruns in the last 24 hours answers 409task_run_limit_reached. A missing task is a 404task_not_found.A key or an app without the scopes of a toolkit the task names is refused with 403
insufficient_scope.A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.Not retried automatically: every call starts another run, so a retry after a lost response can start two. Read
ListRunswithopenemail.WithTaskIDbefore you try again.
Also available in
- API
POST /tasks/{id}/run- TypeScript
tasks.run()- Python
tasks.run()- Ruby
tasks.run- PHP
tasks->run- Java
tasks().run- C#
Tasks.RunAsync- CLI
openemail tasks run
Tasks.ListRuns
List task runs, a page at a time
ListRuns(ctx context.Context, opts ...openemail.RequestOption) (*openemail.Page, error)Returns one page of runs, newest first: every run in the workspace, or the runs of one task with openemail.WithTaskID. ListAllRuns collects every page and IterateRuns walks them lazily. A run is one execution. It belongs to a task, named in taskId and taskName, or it is a one-off job started with StartRun, where both are null.
status is queued until a runner picks the run up, running while it works, waiting while an approval request is open, then done, failed or cancelled. result is the report the run wrote when it finished, and error says why a failed run failed. steps, toolCalls and actions count how often the model was asked what to do next, how many tool calls ran and how many AI actions the run has cost the workspace. GetRun adds the record of each step.
Runs are kept for 90 days.
Parameters
openemail.WithTaskIDstringOnly the runs of this task, by its id. Left out, the runs of every task and the one-off runs are listed.
openemail.WithStatus...stringOnly runs in this state:
queued,running,waiting,done,failedorcancelled.openemail.WithLimitintPage size, from 1 to 100. The server defaults to 25.
openemail.WithCursorstringThe
NextCursorof the previous page, passed back as it came. Leave it out for the first page.openemail.WithAPIKeystringOverrides the client API key for this call only.
Returns
A *openemail.Page with Items, HasMore and NextCursor. Each item has id, taskId, taskName, trigger, threadId, status, instruction, autonomy, model, steps, toolCalls, actions, result, error, errorDetail, pendingApprovals, chatId, createdAt, updatedAt, startedAt and finishedAt.
Example
page, err := client.Tasks.ListRuns(ctx, openemail.WithTaskID("tsk_5c1e9a7b3d2f48e6a0b4c7d1"), openemail.WithStatus("failed"))if err != nil { return err} for _, run := range page.Items { fmt.Println(run.String("id"), run.String("createdAt"), run.String("error"), run.String("errorDetail"))} fmt.Println(page.HasMore, page.NextCursor)Notes
triggersays what started the run:schedule,mailormanual. A run that mail started names the conversation inthreadId.errorisai_limitwhen the workspace ran out of AI actions for the day,ai_unavailablewhen AI is not set up, andattemptsorno_progresswhen the run kept failing or stood still.errorDetailadds a sentence about it.A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.The cursor is opaque and holds where the last row sat in this order, so a run that starts between pages never breaks the walk.
Also available in
- API
GET /task-runs- TypeScript
tasks.listRuns()- Python
tasks.list_runs()- Ruby
tasks.list_runs- PHP
tasks->listRuns- Java
tasks().listRuns- C#
Tasks.ListRunsAsync- CLI
openemail tasks list-runs
Tasks.ListAllRuns
Collect every task run into one slice
ListAllRuns(ctx context.Context, opts ...openemail.RequestOption) ([]openemail.Object, error)Follows NextCursor from page to page and returns with every run that matches, newest first, in the shape ListRuns returns. openemail.WithLimit sets the page size of each request, not the total. A busy workspace keeps a great many runs for its 90 days, so give openemail.WithTaskID or openemail.WithStatus, or prefer IterateRuns when you do not need them all in memory.
Parameters
openemail.WithTaskIDstringOnly the runs of this task, by its id.
openemail.WithStatus...stringOnly runs in this state:
queued,running,waiting,done,failedorcancelled.openemail.WithLimitintPage size for each request, from 1 to 100. The server defaults to 25.
openemail.WithCursorstringStarts the walk after this cursor instead of the first page.
openemail.WithAPIKeystringOverrides the client API key for every page of this walk.
Returns
A []openemail.Object holding every matching run across all pages.
Example
runs, err := client.Tasks.ListAllRuns(ctx, openemail.WithTaskID("tsk_5c1e9a7b3d2f48e6a0b4c7d1"), openemail.WithLimit(100))if err != nil { return err} actions := 0 for _, run := range runs { actions += run.Int("actions")} fmt.Printf("%d runs cost %d AI actions\n", len(runs), actions)Notes
If any page fails the call fails and the runs already fetched are discarded.
A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.
Also available in
Tasks.IterateRuns
Stream the task runs one at a time
IterateRuns(ctx context.Context, opts ...openemail.RequestOption) *openemail.IteratorReturns an iterator that yields runs one by one, newest first, and requests the next page only once the current one is drained. Nothing is fetched until you consume it, and breaking out of the loop stops the requests.
Parameters
openemail.WithTaskIDstringOnly the runs of this task, by its id.
openemail.WithStatus...stringOnly runs in this state:
queued,running,waiting,done,failedorcancelled.openemail.WithLimitintPage size for each request, from 1 to 100. The server defaults to 25.
openemail.WithCursorstringStarts the walk after this cursor instead of the first page.
openemail.WithAPIKeystringOverrides the client API key for every page of this walk.
Returns
An *openemail.Iterator yielding one run per step.
Example
for run, err := range client.Tasks.IterateRuns(ctx, openemail.WithStatus("waiting")).All() { if err != nil { return err } fmt.Println(run.String("id"), run.String("taskName"), "waits for", run.Int("pendingApprovals"), "answers")}Notes
The iterator is lazy, so an abandoned loop costs only the pages you consumed.
A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.
Also available in
Tasks.GetRun
Read one run with every step it took
GetRun(ctx context.Context, id string, opts ...openemail.RequestOption) (openemail.Object, error)Returns one run with everything it did: the fields of a row of ListRuns, the record of the run in transcript, in order, and its approval requests in approvals, newest first. steps stays the count it is in ListRuns.
The entry of transcript with role user holds the instruction the run started with. Each entry with role assistant is one step: the text the assistant wrote and calls, the tools it called at that step. A call names its tool as the MCP server lists it, with the args it was called with, done, which is false while the call waits for approval or has not run yet, and result, what the tool answered.
A run is queued, then running, and waiting while an approval request is open. Read it again until it is done, failed or cancelled. result then holds the report it wrote.
Parameters
idstringRequiredRun id such as
run_2b8d4f6a1c3e5079b6d8f0a2, asListRunsreturns it.openemail.WithAPIKeystringOverrides the client API key for this call only.
Returns
An openemail.Object: the fields of a row of ListRuns, plus transcript, a list of steps that each have role, text, at and calls, and approvals, a list of approval requests in the shape GetApproval returns.
Example
run, err := client.Tasks.GetRun(ctx, "run_2b8d4f6a1c3e5079b6d8f0a2")if err != nil { return err} for _, step := range run.Objects("transcript") { for _, call := range step.Objects("calls") { fmt.Println(step.String("at"), call.String("tool"), call.Bool("done")) }} fmt.Println(run.String("status"), run.String("result"))Notes
A missing run fails with an
*openemail.Errorthat matchesopenemail.ErrNotFoundand codetask_run_not_found. Runs are kept for 90 days, so an older one is missing too.A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.Read only, so the SDK retries it after a network failure like any other read.
Also available in
- API
GET /task-runs/{id}- TypeScript
tasks.getRun()- Python
tasks.get_run()- Ruby
tasks.get_run- PHP
tasks->getRun- Java
tasks().getRun- C#
Tasks.GetRunAsync- CLI
openemail tasks get-run
Tasks.StartRun
Hand the assistant a one-off job to do in the background
StartRun(ctx context.Context, body openemail.Body, opts ...openemail.RequestOption) (openemail.Object, error)Hands the assistant one job to do in the background, with no task saved: a long tidy-up, a report, or anything that should not wait on an open chat. It returns the run as it was queued. Follow it with GetRun, and answer what it asks with Approve or Deny.
instruction is the whole job, in plain words. threadId hands the run one conversation as its starting point. autonomy, toolkits, model and maxSteps mean what they mean on a task and take the same defaults. The workspace owner is notified when the run waits for approval or fails.
A workspace starts at most 100 of these in any 24 hours. A run acts with what its caller holds, so the call needs the same scopes as Create: threads:read, threads:write, emails:read, emails:send and labels:write beside tasks:write, and the read and write scopes of every toolkit it names.
Parameters
instructionstringRequiredWhat the assistant should do, in plain words, trimmed, 1 to 8,000 characters. It is the only instruction the run follows: text inside emails is read, never obeyed.
autonomystringHow much the run may do without asking:
ask,organizeorreply. Defaults toorganize.toolkits[]stringExtra groups of tools the run may use beyond reading and organising mail, such as
contacts,calendar,knowledgeorweb. Left out,drafts,notesandknowledge. An empty slice gives it none.modelstringstandardis the everyday model.advancedreasons better over long jobs and costs ten AI actions where the standard one costs one. Defaults tostandard.maxStepsintThe most model steps the run may take before it has to write its report, from 1 to 60. Defaults to 20.
threadIdstringA conversation to hand the run as its starting point, by the id
Threads.Listreturns.openemail.WithAPIKeystringOverrides the client API key for this call only.
Returns
An openemail.Object with a new id, status queued, trigger manual, and taskId and taskName null. transcript and approvals fill in as the run works.
Example
queued, err := client.Tasks.StartRun(ctx, openemail.Body{ "instruction": "Find every invoice that arrived this month and label it Invoices.", "autonomy": "organize", "maxSteps": 30,})if err != nil { return err} fmt.Println(queued.ID(), queued.String("status"))Notes
Past 100 one-off runs in 24 hours the call answers 409
task_run_limit_reached.A toolkit a run may not use is 422
invalid_task, a value of the wrong type or length 422invalid_parameterwithparamnaming it, and a key the body does not take 422unknown_parameter. A missing scope of a toolkit is a 403insufficient_scope.A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.Not retried automatically: every call starts another run, so a retry after a lost response can start two. Read
ListRunsbefore you try again.
Also available in
- API
POST /task-runs- TypeScript
tasks.startRun()- Python
tasks.start_run()- Ruby
tasks.start_run- PHP
tasks->startRun- Java
tasks().startRun- C#
Tasks.StartRunAsync- CLI
openemail tasks start-run
Tasks.CancelRun
Stop a run that has not ended
CancelRun(ctx context.Context, id string, opts ...openemail.RequestOption) (openemail.Object, error)Stops a run that is queued, running or waiting for approval, and closes its open approval requests, which become cancelled. What the run already did stays done: nothing is rolled back. The task it belongs to is left as it is and runs again when its trigger next fires. It takes no body.
Parameters
idstringRequiredRun id such as
run_2b8d4f6a1c3e5079b6d8f0a2, asListRunsreturns it.openemail.WithAPIKeystringOverrides the client API key for this call only.
Returns
An openemail.Object with status cancelled and finishedAt set, and transcript showing how far it got.
Example
run, err := client.Tasks.CancelRun(ctx, "run_2b8d4f6a1c3e5079b6d8f0a2")if err != nil { return err} fmt.Println(run.String("status"), run.String("finishedAt"), run.Int("toolCalls"))Notes
A run that has already ended answers 409
task_run_finished, and a missing one 404task_run_not_found.To keep a task from starting more runs, use
Pause.A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.Not retried automatically. A 409
task_run_finishedon your own second attempt after a lost response means the first one worked or the run ended by itself, andGetRunsays which.
Also available in
Tasks.ListApprovals
List what runs are waiting for a person to allow
ListApprovals(ctx context.Context, opts ...openemail.RequestOption) (*openemail.Page, error)Returns one page of approval requests, newest first. An approval request is a tool call a run stopped at because it may not make it alone: tool names the tool, summary says in one line what would happen, and args holds the exact arguments it would be called with. A reply shows the recipients worked out from the message it answers.
Without openemail.WithStatus the list holds only the requests still pending. Give it to read the ones that were approved, denied, expired or cancelled, and openemail.WithRunID or openemail.WithTaskID to read those of one run or of one task. ListAllApprovals collects every page and IterateApprovals walks them lazily.
A request waits 7 days. Then it expires and the run carries on without it. Answer one with Approve or Deny.
Parameters
openemail.WithStatus...stringOnly requests in this state:
pending,approved,denied,expiredorcancelled. Left out, the list holds the ones still pending.openemail.WithRunIDstringOnly the requests of this run, by its id.
openemail.WithTaskIDstringOnly the requests of the runs of this task, by its id.
openemail.WithLimitintPage size, from 1 to 100. The server defaults to 25.
openemail.WithCursorstringThe
NextCursorof the previous page, passed back as it came. Leave it out for the first page.openemail.WithAPIKeystringOverrides the client API key for this call only.
Returns
A *openemail.Page with Items, HasMore and NextCursor. Each item has id, runId, taskId, taskName, tool, summary, args, status, createdAt, expiresAt and decidedAt.
Example
page, err := client.Tasks.ListApprovals(ctx)if err != nil { return err} for _, approval := range page.Items { fmt.Println(approval.String("id"), approval.String("taskName"), approval.String("summary"), approval.String("expiresAt"))} fmt.Println(page.HasMore, page.NextCursor)Notes
No value of
openemail.WithStatusmeans the requests still pending. Read each other state in turn to see the rest.taskIdandtaskNameare null on a request of a one-off run.A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.
Also available in
Tasks.ListAllApprovals
Collect every approval request into one slice
ListAllApprovals(ctx context.Context, opts ...openemail.RequestOption) ([]openemail.Object, error)Walks every page of ListApprovals and returns with all the requests that match, newest first. One request per page. Without openemail.WithStatus it collects only the requests still pending, which is the list a person has to answer.
Parameters
openemail.WithStatus...stringOnly requests in this state:
pending,approved,denied,expiredorcancelled. Left out, the ones still pending are collected.openemail.WithRunIDstringOnly the requests of this run, by its id.
openemail.WithTaskIDstringOnly the requests of the runs of this task, by its id.
openemail.WithLimitintPage size for each request, from 1 to 100. The server defaults to 25.
openemail.WithCursorstringStarts the walk after this cursor instead of the first page.
openemail.WithAPIKeystringOverrides the client API key for every page of this walk.
Returns
A []openemail.Object holding every matching request.
Example
pending, err := client.Tasks.ListAllApprovals(ctx, openemail.WithRunID("run_2b8d4f6a1c3e5079b6d8f0a2"))if err != nil { return err} for _, approval := range pending { fmt.Println(approval.String("tool"), approval.String("summary"))}Notes
If any page fails the call fails and the requests already fetched are discarded.
A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.
Also available in
Tasks.IterateApprovals
Stream the approval requests one at a time
IterateApprovals(ctx context.Context, opts ...openemail.RequestOption) *openemail.IteratorReturns an iterator that yields approval requests one by one, newest first, and requests the next page only once the current one is drained. Without openemail.WithStatus it yields only the requests still pending. Nothing is fetched until you consume it, and breaking out of the loop stops the requests.
Parameters
openemail.WithStatus...stringOnly requests in this state:
pending,approved,denied,expiredorcancelled. Left out, the ones still pending are walked.openemail.WithRunIDstringOnly the requests of this run, by its id.
openemail.WithTaskIDstringOnly the requests of the runs of this task, by its id.
openemail.WithLimitintPage size for each request, from 1 to 100. The server defaults to 25.
openemail.WithCursorstringStarts the walk after this cursor instead of the first page.
openemail.WithAPIKeystringOverrides the client API key for every page of this walk.
Returns
An *openemail.Iterator yielding one approval request per step.
Example
for approval, err := range client.Tasks.IterateApprovals(ctx, openemail.WithTaskID("tsk_5c1e9a7b3d2f48e6a0b4c7d1")).All() { if err != nil { return err } fmt.Println(approval.String("id"), approval.String("tool"), approval.String("summary"))}Notes
The iterator is lazy, so an abandoned loop costs only the pages you consumed.
A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.
Also available in
Tasks.GetApproval
Read one approval request by id
GetApproval(ctx context.Context, id string, opts ...openemail.RequestOption) (openemail.Object, error)Looks up a single approval request and returns it in the same shape as a row of ListApprovals: the run that asked and its task, the tool the run wants to call, the one-line summary, the exact args and its status. args are what will be done once it is approved, unless Approve replaces the subject or the words of a send or a reply.
Parameters
idstringRequiredApproval request id such as
apr_9f3a1c5e7b2d4608a1c3e5f7, asListApprovalsreturns it.openemail.WithAPIKeystringOverrides the client API key for this call only.
Returns
An openemail.Object with id, runId, taskId, taskName, tool, summary, args, status, createdAt, expiresAt and decidedAt.
Example
approval, err := client.Tasks.GetApproval(ctx, "apr_9f3a1c5e7b2d4608a1c3e5f7")if err != nil { return err} fmt.Println(approval.String("tool"), approval.String("summary"), approval.Object("args"))Notes
A missing request fails with an
*openemail.Errorthat matchesopenemail.ErrNotFoundand codetask_approval_not_found.taskIdandtaskNameare null on a request of a one-off run.A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.
Also available in
Tasks.Approve
Let a run do what it asked for
Approve(ctx context.Context, id string, body openemail.Body, opts ...openemail.RequestOption) (openemail.Object, error)Approves a pending request, so the run makes the tool call with the arguments the request shows. For a send or a reply, subject and body replace what the run wrote, and the answer shows the changed args. For any other tool they are ignored. Pass nil as the body to approve the request as it stands.
The run picks up again once every request of its step is answered, so a run that asked for three things waits for three answers. A task acts with what its caller holds, so approving needs the scopes the run works with: the same as Create, counted from the toolkits of its task.
Parameters
idstringRequiredApproval request id such as
apr_9f3a1c5e7b2d4608a1c3e5f7, asListApprovalsreturns it.subjectstringFor a send or a reply: the subject to use instead of the one the run wrote, trimmed, 1 to 998 characters.
bodystringFor a send or a reply: the words to send instead of the ones the run wrote, trimmed, 1 to 100,000 characters.
openemail.WithAPIKeystringOverrides the client API key for this call only.
Returns
An openemail.Object with status approved, decidedAt set and args as the tool will be called with them.
Example
approval, err := client.Tasks.Approve(ctx, "apr_9f3a1c5e7b2d4608a1c3e5f7", openemail.Body{ "subject": "Your refund is on its way",})if err != nil { return err} fmt.Println(approval.String("status"), approval.String("decidedAt"))Notes
A request that was already answered, has expired, or whose run has ended answers 409
task_approval_decided. A missing one is a 404task_approval_not_found.There is no undo once the run has acted, so read
argsfirst.A key or an app without the scopes of a toolkit the task names is refused with 403
insufficient_scope, and a key the body does not take is 422unknown_parameter.A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.The SDK does not retry it. A 409
task_approval_decidedon your own second attempt after a lost response means the first one worked or somebody else answered, andGetApprovalsays how.
Also available in
Tasks.Deny
Refuse what a run asked for
Deny(ctx context.Context, id string, opts ...openemail.RequestOption) (openemail.Object, error)Denies a pending request. The run is told, does not try that call again, and carries on with the rest of its task once every request of its step is answered. Nothing the request described is done. It takes no body.
Parameters
idstringRequiredApproval request id such as
apr_9f3a1c5e7b2d4608a1c3e5f7, asListApprovalsreturns it.openemail.WithAPIKeystringOverrides the client API key for this call only.
Returns
An openemail.Object with status denied and decidedAt set.
Example
approval, err := client.Tasks.Deny(ctx, "apr_9f3a1c5e7b2d4608a1c3e5f7")if err != nil { return err} fmt.Println(approval.String("status"), approval.String("tool"))Notes
A request that was already answered, has expired, or whose run has ended answers 409
task_approval_decided. A missing one is a 404task_approval_not_found.To stop the whole run instead of one request, use
CancelRun.A key or an app limited to particular addresses or domains is refused with 422
capability_unsupported, and an app acting for a member whose role does not reach every address with 403owner_only.The SDK does not retry it. A 409
task_approval_decidedon your own second attempt after a lost response means the first one worked or somebody else answered, andGetApprovalsays how.
Also available in
- API
POST /task-approvals/{id}/deny- TypeScript
tasks.deny()- Python
tasks.deny()- Ruby
tasks.deny- PHP
tasks->deny- Java
tasks().deny- C#
Tasks.DenyAsync- CLI
openemail tasks deny