# Bulk operations

> Update as many as 100 clients, tasks, or tickets per request with per-item results, partial success, and the same rules as each single-resource write.

URL: https://www.agencytitan.com/help/guides/bulk-operations/

The public API currently exposes three bulk write operations:

- [`POST /v1/clients/bulk/update`](/docs/tag/clients/POST/v1/clients/bulk/update)
- [`POST /v1/tasks/bulk/update`](/docs/tag/tasks/POST/v1/tasks/bulk/update)
- [`POST /v1/tickets/bulk/update`](/docs/tag/tickets/POST/v1/tickets/bulk/update)

Each bulk operation mirrors one granular write action and applies its exact update shape. It does not introduce a second action enum or new writable fields. Validation, permissions, visibility, and state rules stay aligned with the corresponding single-resource operation:

- The clients bulk operation uses the client update fields, such as `status`, `priority_level`, `tags`, `assigned_to`, and `field_values`.
- The tasks bulk operation uses the `tasks.manage` shape, so each item's `set` requires an `action` of `update_status`, `assign`, or `set_due_date` plus that action's fields.
- The tickets bulk operation uses the ticket update fields, such as `status`, `priority`, `client_id`, and `assigned_to`.

## Request envelope

A bulk write accepts an `items` array in which every item carries its own target ID and its own `set`:

```json
{
  "items": [
    {
      "id": "11111111-1111-4111-8111-111111111111",
      "set": {
        "action": "update_status",
        "status": "completed"
      }
    },
    {
      "id": "22222222-2222-4222-8222-222222222222",
      "set": {
        "action": "set_due_date",
        "due_date": "2026-08-15T17:00:00Z"
      }
    }
  ]
}
```

- `items` is required and accepts 1 through 100 entries.
- Each item's `set` uses the writable fields from the mirrored granular operation. Use the exact `set` schema published for the chosen operation; different bulk operations support different fields.
- When the same ID appears more than once, the first occurrence wins and the resource is processed at most once.
- A request with more than 100 items is rejected at the envelope level. Split larger jobs into batches of at most 100.

The batch is authorized once, then items are processed sequentially in request order through the same code path as the single-resource operation.

## Per-item results

An accepted batch returns HTTP 200 even when some items fail. Inspect every result and the summary:

```json
{
  "data": {
    "results": [
      {
        "id": "11111111-1111-4111-8111-111111111111",
        "success": true
      },
      {
        "id": "22222222-2222-4222-8222-222222222222",
        "success": false,
        "error": {
          "code": "not_found",
          "message": "Resource not found."
        }
      }
    ],
    "summary": {
      "total": 2,
      "succeeded": 1,
      "failed": 1
    }
  }
}
```

Partial success is normal. A per-item failure does not roll back successful items, does not stop later items, and does not fail the batch response. Per-item `error.code` values are `validation_error`, `not_found`, `permission_denied`, `rate_limited`, `conflict`, and `internal`.

Non-200 responses are reserved for envelope-level failures, such as a malformed body, an over-cap request, or a caller that lacks the required permission entirely. If a batch is accepted, retry only the failed IDs after deciding whether each item error is retryable.

## Retries and idempotency

Re-submitting an item runs the single-resource setters again. Values that already match are accepted, but the underlying write may refresh timestamps, task completion validation still applies, and ticket assignment updates may add assignment history. There is no cross-request dedupe, so the caller owns idempotency across batches.

## Integration guidance

- Keep a client-side record of each resource ID and its desired change.
- Persist the returned per-item status before starting the next batch.
- Retry transient failures separately from validation, permission, conflict, and not-found failures.
- Re-read records when state may have changed since the batch was prepared.
- Stay within the API rate-limit headers described in the [API Quickstart](/help/getting-started/api-quickstart/).
