> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nmbr.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Bulk and Batch Operations

Nmbr supports a number of bulk and batch operations for managing multiple recurrences, line items, and other objects in one API call:

* [Bulk Recurrence Operations](./bulk-recurrence-operations)
* [Bulk Line Item Operations](./bulk-line-item-operations)
* [Bulk Work Assignment Operations](./bulk-work-assignment-operations)
* [Bulk Pay Stub Operations](./bulk-pay-stub-operations)
* [Batch Operations](./batch-operations)

The distinction between "bulk" and "batch" is subtle but important.

## Bulk Operations

Bulk operations

* take *criteria objects* that define the criteria used to select the objects to perform the bulk operation on,
* find all objects matching the criteria, and
* perform the same operation on each object.

For example, the bulk operations for line items take criteria objects that define which pay stubs' line items will be affected by the operation.

**Example: Enroll all work assignments in a pay schedule to a CRA remittance account**

`POST /remittance_account_enrollments/bulk/create`

```json theme={null}
{
  "business_entity_id": "<business_entity_id>",
  "work_assignments": {
    "include": {
      "pay_schedule_id": "<pay_schedule_id>"
    }
  },
  "data": {
    "remittance_account_id": "<remittance_account_id>",
    "effective_from": "2026-01-01",
    "overlap_strategy": "replace_existing"
  }
}
```

This creates one enrollment for every work assignment selected by `work_assignments`. Setting `overlap_strategy` to `replace_existing` resolves overlapping enrollments for the same work assignment and provider: enrollments beginning before the new `effective_from` are end-dated, while those beginning on or after it are removed. Omit it and an overlap returns a validation error.

**Example: Create earning line items on all non-contractor pay stubs in a payroll**

`POST /earning_line_items/bulk/create`

```json theme={null}
{
  "payroll_id": "payrl_01J8KX9R2FMQVW3TNZH5Y7B4C6",
  "pay_stubs": {
    "include": "all",
    "exclude": {
      "payee_type": "contractor"
    }
  },
  "data": {
    "earning_type": "bonus_discretionary",
    "custom_amount": 300.0,
    "title": "Christmas Bonus"
  }
}
```

**Example: Update all earning line items with a specific business preset on all pay stubs in a payroll**

`POST /earning_line_items/bulk/update`

```json theme={null}
{
  "payroll_id": "payrl_01J8KX9R2FMQVW3TNZH5Y7B4C6",
  "pay_stubs": {
    "include": "all"
  },
  "business_presets": {
    "include": { "ids": ["rps_01J8KXD3M7RQWN2FXZV9Y4H6B1"] }
  },
  "data": {
    "custom_amount": 500.0
  }
}
```

**Example: Delete all earning line items on all pay stubs in a payroll**

`POST /earning_line_items/bulk/delete`

```json theme={null}
{
  "payroll_id": "payrl_01J8KX9R2FMQVW3TNZH5Y7B4C6",
  "pay_stubs": {
    "include": "all"
  }
}
```

### Scope Endpoints

Every bulk operation has a corresponding **scope** endpoint that previews which entities will be affected by the operation, without performing it. Scope endpoints accept the same request body as their corresponding bulk operation and return the entities that would be affected.

The scope endpoint URL is the bulk operation URL with `/scope` appended:

| Bulk Operation             | Scope Endpoint                   |
| -------------------------- | -------------------------------- |
| `POST /<type>/bulk/create` | `POST /<type>/bulk/create/scope` |
| `POST /<type>/bulk/update` | `POST /<type>/bulk/update/scope` |
| `POST /<type>/bulk/delete` | `POST /<type>/bulk/delete/scope` |

What the scope endpoint returns depends on the operation:

* **Create scope** returns the *parent* entities the new entities will be created under (e.g. pay stubs for line items, work assignments for recurrences, payees for work assignments, work assignments for pay stubs).
* **Update scope** returns the entities that will be updated.
* **Delete scope** returns the entities that will be deleted.

Work assignments don't support a bulk delete, so they have no delete scope endpoint.

See [Bulk Line Item Operations](./bulk-line-item-operations), [Bulk Recurrence Operations](./bulk-recurrence-operations), [Bulk Work Assignment Operations](./bulk-work-assignment-operations), and [Bulk Pay Stub Operations](./bulk-pay-stub-operations) for details on the scope endpoints for each type.

## Batch Operations

Batch operations take one object or ID for each entity to perform the batch operation on.

For example, the batch upsert operations for line items take one object per line item, and will create or update each line item with that object's properties.

For example, the batch delete operations for line items take one ID per line item, and will delete the line item with that ID.

## Comparing Bulk and Batch Operations

Bulk operations take criteria objects and perform the operation on 0..n entities, all with the same properties.

Batch operations take objects or IDs and perform the operation on each entity, 1:1, with different properties per object.

## Asynchronous Execution

All bulk and batch mutating operations are asynchronous: they return `202 Accepted` with an [`async_task`](/api-reference/async-tasks/retrieve-an-async-task) handle. The task may or may not have completed by the time the response arrives — partners must always check `data.status` (poll `links.self`, or subscribe to the [`async_task_completed`](/api/overview/webhook-structure) webhook). See [Batch Operations](./batch-operations#asynchronous-execution), [Bulk Line Item Operations](./bulk-line-item-operations#asynchronous-execution), [Bulk Recurrence Operations](./bulk-recurrence-operations#asynchronous-execution), [Bulk Work Assignment Operations](./bulk-work-assignment-operations#asynchronous-execution), and [Bulk Pay Stub Operations](./bulk-pay-stub-operations#asynchronous-execution) for the per-section details.

Scope endpoints are synchronous previews with no side effects.

### Failed Tasks

The `async_task_completed` webhook fires when the task reaches its final state, whether the work succeeded or failed. It tells you the task is done, not that it worked. The payload carries no status, so fetch the task with the ID from the webhook and read `data.status`:

* `completed`: the work succeeded. `completed_at` is set, and `data.results` lists the affected entities.
* `error`: the work failed. `completed_at` stays `null`.

A batch is atomic, so an errored batch task wrote nothing. The entities it could not write appear in `data.results` with `status` set to `error` and a message in `error`. `id` is `null` when the entity was being created:

```json theme={null}
{
  "id": "asnct_01KS0G8Z2YD3T9KQNFW1XEA7HB",
  "object": "async_task",
  "data": {
    "type": "batch_upsert",
    "status": "error",
    "completed_at": null,
    "results": [
      {
        "id": "ernli_01J8KXC9R4MQVW2FXZN7Y5H3B8",
        "object": "earning_line_item",
        "status": "error",
        "error": "There was an unexpected error when updating this object."
      }
    ],
    "created_at": "2026-05-19T16:14:32Z",
    "updated_at": "2026-05-19T16:14:35Z"
  },
  "links": {
    "self": "/async_tasks/asnct_01KS0G8Z2YD3T9KQNFW1XEA7HB"
  }
}
```

An errored bulk task reports no per-entity detail: `data.results` is empty. The writes run in a single transaction, but the work that follows them (recalculating a payroll's totals, for example) does not, so re-read the entities the operation targeted to see what landed before you retry.
