---
title: "Resolve a review item, or skip an inactive pending/failed item in a paused task (or pending in a draft) after a verified purchase using reason customer_purchased, operationId and evidenceRef; preserves attempts, costs and DNC, with durable resolution readback"
method: POST
path: "/v1/tasks/{id}/items/{itemId}/resolve"
---

# Resolve a review item, or skip an inactive pending/failed item in a paused task (or pending in a draft) after a verified purchase using reason customer_purchased, operationId and evidenceRef; preserves attempts, costs and DNC, with durable resolution readback

`POST /v1/tasks/{id}/items/{itemId}/resolve`

## Request

- Base URL: `https://volai.cz`
- URL: `https://volai.cz/v1/tasks/{id}/items/{itemId}/resolve`
- Auth: HTTP bearer

A separate verified-purchase variant accepts `action: skip`, `reason: customer_purchased`, `revision`, `operationId` and `evidenceRef` (opaque identifiers up to 128 characters each). It skips only an inactive `pending` item in a `draft` task or `pending`/`failed` in a `paused` task with no unsettled calls or reservations. History, costs and DNC are preserved; `GET` returns the immutable `items[].resolution` receipt. Replay the same operation after an uncertain response without another write; a different resolution on an already resolved item returns `409 revision_conflict`. Original behavior without `reason`: Decides about a contact waiting for review (`items[].status: "review"`): normally, `retry` puts it back in the queue without consuming another attempt and `skip` closes it as skipped. With `retryPolicy: "lorela_enterprise"`, manual `retry` returns `validation`; only scheduled automatic attempts are allowed. A contact in `review` can appear while the task is still running, but `resolve` works only while the task is not running - on a running task it returns `409 task_not_editable`, so wait for `needs_attention` or pause the task (`pause`). For `reviewReason: "dial:destination_auto_blocked"` first lift the block (`POST /v1/dnc/{e164}/unblock`) and only then `retry`; for `dial:cannot_call_own_number`, `dial:cannot_call_volai_number`, `dial:invalid_number`, `dial:unsupported_country` and `dial:blocked_destination` only `skip` helps. `reviewReason: "billing_unknown"` is different: the `providerCallId` and reserved budget remain attached until `POST .../reconcile`; after `retry`, the replacement attempt waits for the original call to settle, and after `skip` no replacement call is made but the original call may still be charged later (its reservation keeps counting against the budget). After `retry` the task therefore stays in `needs_attention` until the price settles; after `skip` it moves to `paused` and can be started when work remains, otherwise it waits in `needs_attention` and completes once the price settles. A call that was never dialed settles at 0 right away. For other review reasons, resolving the final contact moves it from `needs_attention` to `paused` (`start` resumes it) or `completed`.

## Path parameters

- `id` string, required
- `itemId` string, required

## Request body

- union
  - object
    - `action` 'retry' | 'skip', required: retry returns a reviewed item to the queue; skip finishes it without another call. Tasks with lorela_enterprise reject manual retry with validation; only scheduled automatic attempts are allowed. Resolving billing_unknown may leave the item pending until reconciliation confirms the existing call, to prevent duplicate billing.
    - `revision` integer, required: Current task revision from GET /v1/tasks/{id}. On revision_conflict reload and review the new state before retrying. Reconciliation accepts omission; other task changes require the current revision.
  - object
    - `action` 'skip', required: retry returns a reviewed item to the queue; skip finishes it without another call. Tasks with lorela_enterprise reject manual retry with validation; only scheduled automatic attempts are allowed. Resolving billing_unknown may leave the item pending until reconciliation confirms the existing call, to prevent duplicate billing.
    - `revision` integer, required: Current task revision from GET /v1/tasks/{id}. On revision_conflict reload and review the new state before retrying. Reconciliation accepts omission; other task changes require the current revision.
    - `reason` 'customer_purchased', required: Skip after a verified purchase. Only an inactive pending draft item or pending/failed paused item is eligible; this does not resolve review items.
    - `operationId` string, required: Stable opaque id for this purchase-skip operation; reuse it after an uncertain response.
    - `evidenceRef` string, required: Opaque purchase reference without customer details; caller must verify the purchase before requesting this skip.

## Response `200`

Task after the decision

- object
  - `task` object, required
    - `purpose` 'standard' | 'coupon_reminder', required: Immutable task purpose. Coupon reminders require manual/custom, maxAttempts 1, expiresAt and a trusted server preflight.
    - `id` string, required
    - `name` string, required
    - `agentId` string, required
    - `taskType` 'invoice' | 'appointment' | 'custom', required
    - `source` 'manual' | 'integration', required
    - `externalRefs` object
      - `invoice` object
        - `connectionId` string, required
        - `externalId` string, required
        - `provider` 'fakturoid' | 'abra-flexi'
      - `calendar` object
        - `connectionId` string, required
        - `calendarId` string, required
        - `eventId` string, required
        - `provider` 'google-calendar' | 'apple-calendar'
    - `callingWindow` object, required
      - `timezone` 'Europe/Prague', required
      - `start` string, required
      - `end` string, required
      - `days` number[], required
    - `maxAttempts` union, required: Maximum automatic attempts including the first. Values 1-3 without a retry policy; 1-4 with lorela_enterprise.
      - 1
      - 2
      - 3
      - 4
    - `retryPolicy` 'lorela_enterprise': Opt-in retry schedule. Absent on tasks using the existing retry behavior.
    - `dispatchOrder` 'first_attempts_first': Prioritize due recipients without a counted attempt before due retries. Pre-dispatch rejections do not count. Omitted preserves item order.
    - `expiresAt` integer: Unix milliseconds after which no new call may begin, including the first attempt. Does not interrupt an active call.
    - `maxDurationSecs` number, required
    - `budgetHal` number, required
    - `reservedBudgetHal` number, required
    - `spentHal` number, required
    - `status` 'draft' | 'ready' | 'running' | 'paused' | 'completed' | 'needs_attention', required
    - `revision` number, required
    - `createdAt` number, required
    - `updatedAt` number, required
    - `lastError` string
    - `reviewReason` string
    - `activeItemId` string
    - `items` object[], required
      - `id` string, required
      - `phone` string, required
      - `variables` object, required
      - `externalRefs` object
        - `invoice` object
          - `connectionId` string, required
          - `externalId` string, required
          - `provider` 'fakturoid' | 'abra-flexi'
        - `calendar` object
          - `connectionId` string, required
          - `calendarId` string, required
          - `eventId` string, required
          - `provider` 'google-calendar' | 'apple-calendar'
      - `paymentStatus` 'not_applicable' | 'unknown' | 'unpaid' | 'paid' | 'disputed', required
      - `status` 'pending' | 'in_progress' | 'completed' | 'skipped' | 'failed' | 'review', required
      - `attempts` object[], required
        - `id` string, required
        - `startedAt` number, required
        - `finishedAt` number
        - `providerCallId` string
        - `outcome` 'placed' | 'completed' | 'no_answer' | 'busy' | 'failed' | 'unknown'
        - `countsAsAttempt` boolean
        - `errorCode` string
        - `costHal` number
        - `variables` object
        - `goalResult` 'success' | 'failure' | 'unknown'
        - `statement` 'promised' | 'disputed' | 'none' | 'unknown'
        - `nextStep` string
      - `nextAttemptAt` number
      - `resolution` object
        - `action` 'skip', required
        - `reason` 'customer_purchased', required
        - `operationId` string, required
        - `evidenceRef` string, required
        - `fingerprint` string, required
        - `resolvedAt` number, required
      - `lastError` string
      - `reviewReason` string
      - `providerCallId` string
      - `estimatedCostHal` number
      - `reservedCostHal` number
      - `actualCostHal` number

## Other responses

- `400`: Error codes: validation (request).
- `401`: Error codes: unauthorized (request).
- `403`: Error codes: account_suspended (account).
- `404`: Error codes: task_not_found (account).
- `409`: Error codes: task_not_editable (account), item_not_resolvable (account), revision_conflict (request).
- `429`: Error codes: rate_limited (busy).
- `500`: Error codes: internal_error (service).

---

[API](https://skmtc.dev/volai/apis/volai-rest-api.md) · [All operations](https://skmtc.dev/volai/apis/volai-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/volai/apis/volai-rest-api/revisions/12029b05e2fa?raw)
