GuidesAutomation and operations

Run a background Task

Use an on-demand Task when an Agent should complete work asynchronously without a schedule. This guide creates the definition, submits one run, and verifies both its status and transcript.

Outcome

You will obtain a terminal Task run and its final assistant message. The main path expects succeeded; production code must also handle blocked, failed, and canceled.

Before you begin

You need an enabled Agent, its agentId, a configured backend SDK client, and a fixed instruction for the Task. Review Tasks and schedules, Sessions and Turns, and Versions and lifecycle.

Create an on-demand Task

Create the Task with agentId, name, and prompt. Omitting schedule stores null; enabled, submit, userId, metadata, and agentVersion default to true, false, "", {}, and null. A null agentVersion resolves the Agent's latest Version when each run is enqueued. Set a Version number to Pin future runs of this Task.

Submit an idempotent run

Pass a stable, Task-scoped business key to createRun. Repeating the same key for the same tenant and Task resolves to the same run ID. A different submission while that Task has a queued or running run is rejected because only one run can be active.

Wait for completion

Poll getRun with a bounded loop. queued and running are active; blocked, succeeded, failed, and canceled are terminal.

Read the transcript

Every executing run receives a fresh Session. runMessages returns its UIMessage transcript; the final response is the last message whose role is assistant. A run blocked before Session creation has an empty transcript.

Treat run.error as an untrusted, potentially sensitive arbitrary error message. Redact it before logging or sharing; do not interpolate it into a newly thrown error.

Cancel an active run

To exercise cancellation, use this five-line branch with the primary example's waitForTerminal helper. It submits a fresh run and immediately requests cancellation while the run can still be active. Missing, mismatched, already terminal, and repeated cancellations are no-op 204 responses for an owned Task.

const cancelInput = { idempotencyKey: `weekly-report-cancel:${weekStart}` };
const cancellation = await client.tasks.createRun(task.id, cancelInput);
await client.tasks.cancelRun(task.id, cancellation.runId);
const canceled = await waitForTerminal(task.id, cancellation.runId);
console.log(canceled.status);

Cancellation can race normal completion; branch on the terminal status you observe instead of assuming canceled.

Verify the terminal state

const terminal = new Set(["blocked", "succeeded", "failed", "canceled"]);
async function waitForTerminal(taskId: string, runId: string) {
  let run = await client.tasks.getRun(taskId, runId);
  for (let attempt = 0; attempt < 60 && !terminal.has(run.status); attempt++) {
    await new Promise((resolve) => setTimeout(resolve, 1_000));
    run = await client.tasks.getRun(taskId, runId);
  }
  if (!terminal.has(run.status)) throw new Error("Task polling timed out");
  return run;
}
const { task } = await client.tasks.create({
  agentId,
  name: "Build weekly report",
  prompt: "Build the weekly report and summarize the result.",
});
const { runId } = await client.tasks.createRun(task.id, {
  idempotencyKey: `weekly-report:${weekStart}`,
});
const run = await waitForTerminal(task.id, runId);
const messages = await client.tasks.runMessages(task.id, runId);
const finalResponse = messages.data.findLast(
  (message) => message.role === "assistant",
);
if (run.status !== "succeeded" || !finalResponse) {
  throw new Error(`Task verification failed with status ${run.status}`);
}
console.log(finalResponse.parts);

The check proves that the documented terminal state and at least one assistant transcript message are both observable without propagating the raw run error.

Production notes

  • Keep idempotency keys stable across client retries. Task creation and submit: true are not idempotent; create the Task first, then use createRun when submission deduplication matters.
  • Task execution is at-most-once. Recovery returns a recorded completed outcome without rerunning the Turn; an interrupted claimed Turn is finalized as failed rather than replaying model or Tool effects.
  • Updating agentVersion changes the Pin for future runs. null restores latest-Version resolution; each run records the Version it actually used.
  • A Task is preflighted before Session creation and checked again at the normal Turn gate. Quota denial ends as blocked, not failed.
  • Read Limits and reliability and Usage and quotas before setting retry and alert policy.

SDK and REST reference

See the TypeScript SDK operations for create, createRun, getRun, runMessages, and cancelRun. The matching REST contracts are Tasks, create Task run, get Task run, list Task run messages, and cancel Task run.

On this page