> ## 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 Pay Stub Operations

Bulk pay stub operations create, update, or delete many pay stubs in one API call. They apply only to payrolls that accept manual pay stub management: once-off, historical, correction, and custom-schedule payrolls in `draft` status. Regular scheduled payrolls generate their pay stubs automatically, so you can't bulk-create on them.

Selection differs between create and update/delete:

* **Create** selects *work assignments* on the payroll's pay schedule and creates one pay stub per work assignment.
* **Update** and **delete** select *pay stubs* that already exist on the payroll.

## Asynchronous Execution

Bulk pay stub operations are asynchronous. Every bulk endpoint returns an [`async_task`](/api-reference/async-tasks/retrieve-an-async-task) handle that you use to track completion and retrieve the affected entities.

The task **may or may not have completed** by the time you receive the response. `data.status` could be `processing` or `completed`. **Partners must always check `data.status` and not assume the work has finished.**

You have two ways to observe completion:

* **Poll** the URL in `links.self` on the response until `data.status` is `completed` (or `error`).
* **Subscribe** to the [`async_task_completed`](/api/overview/webhook-structure) webhook event, which fires when the task finishes.

`data.results` contains one entry per affected pay stub, with only the `id` and `object`. To retrieve full pay stub bodies, follow up with a [list query](/api-reference/pay-stubs/list-pay-stubs) (filtered by the returned IDs) or fetch individual pay stubs by ID.

Note: scope endpoints (`/bulk/*/scope`) are previews with no side effects and are synchronous - they return `200 OK` with the inline list of matched entities.

## Bulk Create Pay Stubs

Nmbr's API allows you to create a pay stub for select work assignments on a single payroll. This endpoint is useful when you need to add a group of employees or contractors to an off-cycle or historical payroll in one API call.

Create selects work assignments rather than pay stubs, because the pay stubs don't exist yet. The work assignments come from the payroll's pay schedule.

### Endpoint

`POST /pay_stubs/bulk/create`

### Request

```json theme={null}
{
  "payroll_id": ULID,
  "work_assignments": {
    "include": "all" | criteria object,
    "exclude": criteria object (optional)
  },
  "data": {
    ...pay stub properties...
  }
}
```

#### Request Properties

| Property                   | Type                       | Required | Description                                        |
| -------------------------- | -------------------------- | -------- | -------------------------------------------------- |
| `payroll_id`               | ULID                       | Yes      | ID of the payroll to create the pay stubs under    |
| `work_assignments.include` | criteria object or `"all"` | Yes      | Criteria for selecting work assignments to include |
| `work_assignments.exclude` | criteria object            | No       | Criteria for selecting work assignments to exclude |
| `data`                     | object                     | No       | The properties to apply to each created pay stub   |

#### Work Assignment Criteria Objects

`include` and `exclude` can be objects with one or more of the following properties:

* `ids`: Array of work assignment IDs to include or exclude
* `payee_type`: `"employee"` or `"contractor"`
* `pay_schedule_id`: ID of the pay schedule to include or exclude
* `archived`: `true` to select archived work assignments, `false` (default) to select active ones
* `payee_names`: Name search (partial match)

When you combine properties, they apply together.

`include` can also be the string `"all"` to include all work assignments on the payroll's pay schedule.

#### Data Properties

The `data` object contains the pay stub properties to apply to each created pay stub:

| Property         | Description                                                                                     |
| ---------------- | ----------------------------------------------------------------------------------------------- |
| `payment_method` | Payment method for the pay stub. Must match the business entity's locked payment method, if set |
| `note`           | A note on the pay stub                                                                          |
| `external_ref`   | Your external identifier for the pay stub                                                       |

The payroll and work assignment for each pay stub come from your selection. Don't set `payroll_id` or `work_assignment_id` in `data`. These properties are documented in more detail in the API reference for pay stubs.

### Response

On success, the operation returns `202 Accepted` with an [`async_task`](/api-reference/async-tasks/retrieve-an-async-task) handle. See [Asynchronous Execution](#asynchronous-execution) for how to track completion. **⚠️ Partners must treat any 200-level response as success. ⚠️**

For example, the response might look like:

```json theme={null}
{
  "id": "asnct_01KS0G8Z2YD3T9KQNFW1XEA7HB",
  "object": "async_task",
  "data": {
    "type": "bulk_create",
    "status": "processing",
    "completed_at": null,
    "results": [],
    "created_at": "2026-05-19T16:14:32Z",
    "updated_at": "2026-05-19T16:14:32Z"
  },
  "links": {
    "self": "/async_tasks/asnct_01KS0G8Z2YD3T9KQNFW1XEA7HB"
  }
}
```

Once the task completes, `data.results` will contain one entry per created pay stub:

```json theme={null}
{
  "id": "asnct_01KS0G8Z2YD3T9KQNFW1XEA7HB",
  "object": "async_task",
  "data": {
    "type": "bulk_create",
    "status": "completed",
    "completed_at": "2026-05-19T16:14:35Z",
    "results": [
      { "id": "payst_01J8KXC9R4MQVW2FXZN7Y5H3B8", "object": "pay_stub" },
      { "id": "payst_01KBMZDVV9G7713DJRYP9RJFTP", "object": "pay_stub" }
    ],
    "created_at": "2026-05-19T16:14:32Z",
    "updated_at": "2026-05-19T16:14:35Z"
  },
  "links": {
    "self": "/async_tasks/asnct_01KS0G8Z2YD3T9KQNFW1XEA7HB"
  }
}
```

On failure, the operation will return `422 Unprocessable Entity` with standard validation messages.

### Notes

* The payroll must be a once-off, historical, correction, or custom-schedule payroll in `draft` status. Regular scheduled payrolls are rejected.
* The same pay stub properties are applied to every created pay stub.
* Work assignments that already have a pay stub on the payroll are skipped. There is one pay stub per work assignment per payroll. Skipped work assignments won't appear in `data.results`.
* Each created pay stub's totals are recalculated after creation, populating any managed line items the work assignment's configuration produces.
* Validation runs against every selected work assignment before any pay stub is created. If validation fails, no pay stubs are created.
* Invalid work assignment IDs in the `include.ids` or `exclude.ids` arrays are silently ignored and won't cause an error.
* When both `include` and `exclude` criteria are provided, exclusions are applied after inclusions.

### Examples

#### Create a pay stub for every work assignment on the payroll's pay schedule

`POST /pay_stubs/bulk/create`

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

#### Create pay stubs for specific work assignments with a note

`POST /pay_stubs/bulk/create`

```json theme={null}
{
  "payroll_id": "payrl_01J8KX9R2FMQVW3TNZH5Y7B4C6",
  "work_assignments": {
    "include": {
      "ids": [
        "wrkas_01J8KXB4N6RQWM2FVZH9Y3T5C8",
        "wrkas_01J8KXB7P2MQVW4RXZN6Y8H3F1"
      ]
    }
  },
  "data": {
    "note": "Q4 bonus run"
  }
}
```

#### Create pay stubs for all employees, excluding contractors

`POST /pay_stubs/bulk/create`

```json theme={null}
{
  "payroll_id": "payrl_01J8KX9R2FMQVW3TNZH5Y7B4C6",
  "work_assignments": {
    "include": "all",
    "exclude": {
      "payee_type": "contractor"
    }
  }
}
```

## Bulk Create Pay Stubs Scope

The create scope endpoint previews which work assignments will receive a new pay stub from a bulk create operation. It accepts the same request body as the bulk create endpoint, but instead of creating pay stubs, it returns the work assignments that match the criteria.

Because the pay stubs don't exist yet, this endpoint returns *work assignments*, not pay stubs. Work assignments that already have a pay stub on the payroll are excluded, matching the create behavior.

This is useful for confirming which work assignments a bulk create will affect before executing it.

### Endpoint

`POST /pay_stubs/bulk/create/scope`

### Request

The request body is the same as the [bulk create](#bulk-create-pay-stubs) endpoint.

### Response

On success, the operation will return `200 OK` with an array of the work assignments that the bulk create would create pay stubs for:

```json theme={null}
{
  "data": [
    {
      "id": "wrkas_01J8KXB4N6RQWM2FVZH9Y3T5C8",
      "data": {
        ...work assignment properties...
      }
    },
    ...
  ]
}
```

## Bulk Update Pay Stubs

Nmbr's API allows you to update select pay stubs within a single payroll. This endpoint is useful when you need to set the same property on a group of pay stubs in one API call, such as adding a note or changing the payment method.

### Endpoint

`POST /pay_stubs/bulk/update`

### Request

```json theme={null}
{
  "payroll_id": ULID,
  "pay_stubs": {
    "include": "all" | criteria object,
    "exclude": criteria object (optional)
  },
  "data": {
    ...properties to update...
  }
}
```

#### Request Properties

| Property            | Type                       | Required | Description                                        |
| ------------------- | -------------------------- | -------- | -------------------------------------------------- |
| `payroll_id`        | ULID                       | Yes      | ID of the payroll containing the pay stubs         |
| `pay_stubs.include` | criteria object or `"all"` | Yes      | Criteria for selecting pay stubs to include        |
| `pay_stubs.exclude` | criteria object            | No       | Criteria for selecting pay stubs to exclude        |
| `data`              | object                     | Yes      | The properties to update on the matching pay stubs |

#### Pay Stub Criteria Objects

`include` and `exclude` can be objects with one or more of the following properties:

* `ids`: Array of pay stub IDs to include or exclude
* `work_assignment_ids`: Array of work assignment IDs. Selects the pay stubs for those work assignments
* `payee_type`: `"employee"` or `"contractor"`

`include` can also be the string `"all"` to include all pay stubs on the payroll.

#### Data Properties

The `data` object contains the pay stub properties to update:

| Property         | Description                                                                                     |
| ---------------- | ----------------------------------------------------------------------------------------------- |
| `payment_method` | Payment method for the pay stub. Must match the business entity's locked payment method, if set |
| `note`           | A note on the pay stub                                                                          |
| `external_ref`   | Your external identifier for the pay stub                                                       |

### Response

On success, the operation returns `202 Accepted` with an [`async_task`](/api-reference/async-tasks/retrieve-an-async-task) handle whose `data.results` enumerates the updated pay stubs. See [Asynchronous Execution](#asynchronous-execution) for how to track completion and [Bulk Create Pay Stubs › Response](#response) for an example response shape. `data.type` will be `bulk_update`. **⚠️ Partners must treat any 200-level response as success. ⚠️**

On failure, the operation will return `422 Unprocessable Entity` with standard validation messages. If any matched pay stub fails per-model validation, the entire request fails and no pay stubs are updated.

### Notes

* The payroll must be a once-off, historical, correction, or custom-schedule payroll in `draft` status. Regular scheduled payrolls are rejected.
* The same update properties are applied to all selected pay stubs.
* The operation only affects pay stubs within the specified payroll.
* Invalid pay stub IDs in the `include.ids` or `exclude.ids` arrays are silently ignored and won't cause an error.
* When both `include` and `exclude` criteria are provided, exclusions are applied after inclusions.

### Examples

#### Add a note to every pay stub in a payroll

`POST /pay_stubs/bulk/update`

```json theme={null}
{
  "payroll_id": "payrl_01J8KX9R2FMQVW3TNZH5Y7B4C6",
  "pay_stubs": {
    "include": "all"
  },
  "data": {
    "note": "Reviewed by payroll admin"
  }
}
```

#### Update the pay stubs for specific work assignments

`POST /pay_stubs/bulk/update`

```json theme={null}
{
  "payroll_id": "payrl_01J8KX9R2FMQVW3TNZH5Y7B4C6",
  "pay_stubs": {
    "include": {
      "work_assignment_ids": ["wrkas_01J8KXB4N6RQWM2FVZH9Y3T5C8"]
    }
  },
  "data": {
    "external_ref": "run-2026-04-offcycle"
  }
}
```

## Bulk Update Pay Stubs Scope

The update scope endpoint previews which pay stubs will be updated by a bulk update operation. It accepts the same request body as the bulk update endpoint, but instead of updating pay stubs, it returns the pay stubs that match the criteria.

### Endpoint

`POST /pay_stubs/bulk/update/scope`

### Request

The request body is the same as the [bulk update](#bulk-update-pay-stubs) endpoint.

### Response

On success, the operation will return `200 OK` with an array of the pay stubs that would be updated:

```json theme={null}
{
  "data": [
    {
      "id": "payst_01J8KXC9R4MQVW2FXZN7Y5H3B8",
      "data": {
        ...pay stub properties...
      }
    },
    ...
  ]
}
```

## Bulk Delete Pay Stubs

Nmbr's API allows you to delete select pay stubs within a single payroll. This endpoint is useful when you need to remove a group of pay stubs from an off-cycle or historical payroll in one API call.

### Endpoint

`POST /pay_stubs/bulk/delete`

### Request

```json theme={null}
{
  "payroll_id": ULID,
  "pay_stubs": {
    "include": "all" | criteria object,
    "exclude": criteria object (optional)
  }
}
```

#### Request Properties

| Property            | Type                       | Required | Description                                 |
| ------------------- | -------------------------- | -------- | ------------------------------------------- |
| `payroll_id`        | ULID                       | Yes      | ID of the payroll containing the pay stubs  |
| `pay_stubs.include` | criteria object or `"all"` | Yes      | Criteria for selecting pay stubs to include |
| `pay_stubs.exclude` | criteria object            | No       | Criteria for selecting pay stubs to exclude |

#### Pay Stub Criteria Objects

`include` and `exclude` can be objects with one or more of the following properties:

* `ids`: Array of pay stub IDs to include or exclude
* `work_assignment_ids`: Array of work assignment IDs. Selects the pay stubs for those work assignments
* `payee_type`: `"employee"` or `"contractor"`

`include` can also be the string `"all"` to include all pay stubs on the payroll.

### Response

On success, the operation returns `202 Accepted` with an [`async_task`](/api-reference/async-tasks/retrieve-an-async-task) handle whose `data.results` enumerates the deleted pay stubs (still surfaced by ID - the records soft-delete and remain queryable). See [Asynchronous Execution](#asynchronous-execution) for how to track completion and [Bulk Create Pay Stubs › Response](#response) for an example response shape. `data.type` will be `bulk_delete`. **⚠️ Partners must treat any 200-level response as success. ⚠️**

On failure, the operation will return `422 Unprocessable Entity` with standard validation messages.

### Notes

* The payroll must be a once-off, historical, correction, or custom-schedule payroll in `draft` status. Regular scheduled payrolls are rejected.
* The operation only affects pay stubs within the specified payroll.
* Invalid pay stub IDs in the `include.ids` or `exclude.ids` arrays are silently ignored and won't cause an error.
* When both `include` and `exclude` criteria are provided, exclusions are applied after inclusions.

### Examples

#### Delete every pay stub in a payroll

`POST /pay_stubs/bulk/delete`

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

#### Delete pay stubs for specific work assignments

`POST /pay_stubs/bulk/delete`

```json theme={null}
{
  "payroll_id": "payrl_01J8KX9R2FMQVW3TNZH5Y7B4C6",
  "pay_stubs": {
    "include": {
      "work_assignment_ids": [
        "wrkas_01J8KXB4N6RQWM2FVZH9Y3T5C8",
        "wrkas_01J8KXB7P2MQVW4RXZN6Y8H3F1"
      ]
    }
  }
}
```

## Bulk Delete Pay Stubs Scope

The delete scope endpoint previews which pay stubs will be deleted by a bulk delete operation. It accepts the same request body as the bulk delete endpoint, but instead of deleting pay stubs, it returns the pay stubs that match the criteria.

### Endpoint

`POST /pay_stubs/bulk/delete/scope`

### Request

The request body is the same as the [bulk delete](#bulk-delete-pay-stubs) endpoint.

### Response

On success, the operation will return `200 OK` with an array of the pay stubs that would be deleted:

```json theme={null}
{
  "data": [
    {
      "id": "payst_01J8KXC9R4MQVW2FXZN7Y5H3B8",
      "data": {
        ...pay stub properties...
      }
    },
    ...
  ]
}
```
