Skip to content

Guides

Tasks

A task dials a list of recipients with one agent, following rules you set up front - budget, calling window, number of attempts. Nothing gets dialed until you start the task yourself.

What a task is

A task (task_...) is an outbound campaign: one agent on the volai engine, a list of recipients with a phone number and optional variables for the system prompt, and rules (budget, calling window, maximum attempts and call duration). Three types by purpose - invoice (payment reminder), appointment (appointment confirmation) and custom (anything else) - for the first two, when source is integration, every recipient additionally carries a reference to the invoice or event (externalRefs) that the agent mentions during the call.

The agent must be on volai's own engine (useForOutboundTasks: true at creation, see POST /v1/agents), not ElevenLabs - the engine supports system-prompt variables, call-duration caps and verification that its configuration hasn't changed while the campaign was running. The agent's draft must be PUBLISHED (PATCH /v1/agents/{id} or POST /v1/agents/{id}/draft/publish) AND the published version must be TESTED (POST /v1/agents/{id}/simulate) - skip either step and start returns agent_not_ready.

Statuses and transitions

In practice a task always goes from `draft` straight to `running` (`start`); deleting (`DELETE`) only works in the marked statuses.

draft
What it meansJust created, never started yet. Editable (PATCH), deletable.
ready
What it meansA model status between draft and running that start accepts as a valid starting point - no public endpoint (REST or MCP) actually moves a task into it today, so you won't encounter it in practice. If it did occur, it's editable and deletable just like draft.
running
What it meansDialing recipients. NOT EDITABLE - PATCH returns task_not_editable, no exception. Anyone who needs to change variables must stop the task first (POST .../pause) and edit it while paused - and only while no recipient has been dialed yet; after the first attempt recipients can no longer be replaced (items_immutable), so create a new task instead. Cannot be deleted.
paused
What it meansStopped before the next call (POST .../pause); a call already in flight finishes. Editable, deletable, start resumes it.
completed
What it meansEvery recipient has a final outcome. Deletable (nothing left to charge).
needs_attention
What it meansSomething needs a look - typically the agent's configuration changed while running (agent_version_changed) or the engine was temporarily unreachable (engine_contract_unavailable). POST .../reconcile reconciles the state; deletable as long as no item is still in_progress.

The exact delete condition: the status is one of the marked ones AND no item has status: "in_progress" (a call that started dialing and hasn't finished) - otherwise task_not_deletable.

Recipient import (CSV)

POST /v1/tasks/csv-preview parses a CSV and returns it back with per-row errors - NOTHING is saved, it's just a preview before creating or updating a task. The new-task form in the portal calls the same function.

  • The delimiter is detected automatically (comma or semicolon) from the first line.
  • A phone column must exist (case-insensitive) - otherwise the whole CSV fails with a phone error.
  • Every other column is a variable for the agent's system prompt ({{column_name}}) - at most 20 columns, name up to 64 characters, value up to 512 characters.
  • The phone number is normalized the same way as everywhere in the API (normalizePhone) - an invalid or empty number is an error only on THAT row, the rest of the CSV is still processed.
  • A cell that looks like a spreadsheet formula (starts with =, +, - or @) is rejected - protection against CSV injection when the file is opened in Excel.
  • At most 100 rows per CSV (the same cap as a task's recipient count) and 1 MB of raw text.

The response carries headers, delimiter, rows (each row with its own errors), validRows (only the clean ones, in exactly the shape recipients in POST /v1/tasks expects) and a summary errors array. Send validRows straight through as recipients when creating or updating the task.

Budget and calling window

Budget

budgetHal is the spending CAP for the whole task, in hellers. At creation it must cover at least one attempt at the maximum duration (budget_too_low otherwise); at start, before EVERY dial the estimated call price is reserved (reservedBudgetHal), and only an answered call turns it into spentHal. A task never spends more than the budgetHal you gave it.

Calling window

callingWindow is {timezone: "Europe/Prague", start, end, days} - today the only supported timezone is Prague, start/end as HH:MM, and days are numbers 0-6 (0 = Sunday). Without one, weekdays 9:00-18:00 apply. Outside the window, start returns window_closed - either wait for it to reopen, or change the window with PATCH.

Attempts and call duration

maxAttempts (1-3) says how many times an unanswered number is retried; maxDurationSecs (30-300) caps the length of ONE call, after which it ends automatically - both optional, the engine uses sensible defaults without them.

Global throughput

Dialing for every task on the WHOLE platform (not just your account) goes through one cron job that runs every 2 minutes and processes at most 20 items per run - BUT that cap is shared across concurrently running tasks, not something a single task can use up on its own: exactly ONE recipient is dialed from EACH active task per run. So even on a completely empty platform, one task never dials more than one recipient every 2 minutes - 100 recipients therefore doesn't finish in ~20 minutes but in roughly 200 minutes (100 x 2 minutes). Plan the calling window for time-sensitive campaigns accordingly (for example an appointment reminder the day before).

Flow for an agent

A typical run, in this order:

  1. The task's agent must be PUBLISHED and the published version TESTED (POST /v1/agents/{id}/draft/publish, then POST /v1/agents/{id}/simulate) - otherwise a later start returns agent_not_ready.
  2. Optionally POST /v1/tasks/csv-preview - checks the recipient CSV before creating the task.
  3. POST /v1/tasks - creates the task in draft status. Nothing gets dialed.
  4. GET /v1/tasks/{id} - read issues (dry-run validation) and confirm the task is ready to start.
  5. As needed, PATCH /v1/tasks/{id} - adjust the rules, ALWAYS with the revision from the last read.
  6. POST /v1/tasks/{id}/start - starts dialing. Every answered call is billed immediately.
  7. POST /v1/tasks/{id}/pause at any point while it runs; POST /v1/tasks/{id}/start again resumes it.
  8. After an interruption (an outage, needs_attention), POST /v1/tasks/{id}/reconcile reconciles the state against the actual calls.
  9. DELETE /v1/tasks/{id} at the end, if the task never ran or already finished and you want the record gone.

Almost every write call (PATCH, start, pause, DELETE) takes revision from the last read of the task - the same optimistic concurrency as agent drafts. A mismatch returns revision_conflict: read the task again (GET) and repeat with the current revision. reconcile is the exception - its revision is optional.

MCP

The same seven tools as REST (list_tasks, get_task, create_task, update_task, pause_task, reconcile_task, start_task) - except for one difference at start.

start_task confirms the numbers

MCP start_task additionally takes confirmedRecipients and confirmedBudgetHal - they must match the task's actual recipient count and budget exactly, otherwise it returns validation with the real values spelled out and starts NOTHING. A third-party AI agent acting on one loosely understood instruction can't accidentally launch a paid campaign on made-up numbers this way - REST POST .../start has no such mechanism, because there the account owner calls it directly with their own key.

delete_task and the CSV preview are not in MCP - deletion stays in REST and the portal (an irreversible action that requires typing the name), and the CSV preview is just a form step.

Errors

  • task_not_found - No task with this id exists on the account.
  • task_not_editable - The task is running or completed, or is needs_attention without a way to recover - its rules can't be changed right now.
  • items_immutable - The recipient list can't be replaced once at least one dial attempt has happened.
  • budget_too_low - The budget doesn't cover even one attempt at the maximum call duration - raise budgetHal or lower maxDurationSecs.
  • task_not_startable - The task isn't in draft/ready/paused status, or has no recipient left to dial.
  • task_needs_attention - A POST .../reconcile is needed before the next start - something (the agent version, the engine) changed while it ran.
  • agent_not_ready - The agent's draft is either not published or the published version hasn't been tested - publish it (POST /v1/agents/{id}/draft/publish) and then simulate the published version (POST /v1/agents/{id}/simulate) before starting.
  • agent_version_changed - The agent's configuration changed WHILE the task was running - test it and restart via reconcile.
  • engine_required - The task's agent is not on the volai engine (useForOutboundTasks: true) - outbound tasks require the engine today, not ElevenLabs.
  • engine_contract_unavailable - The engine does not currently advertise support for outbound-task variables and caps - try starting again later.
  • window_closed - Outside the configured calling window - wait for it to reopen, or change callingWindow.
  • task_not_running - Pausing only works on a running task.
  • task_not_deletable - Only a task without an item in progress, in draft/ready/paused/completed/needs_attention, can be deleted.
  • revision_conflict - The task changed in the meantime - read it again (GET) and repeat with the current revision.

The full error table for every endpoint is in the Tasks section of the REST API reference.