Skip to main content

Asynchronous Execution

Batch operations are asynchronous. Every batch endpoint returns 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 webhook event, which fires when the task finishes.

Batch Upsert

Nmbr’s API allows you to create or update (“upsert”) multiple entities of a single type in a single API call.

Endpoint

POST /<type>s/batch/upsert <type> can be one of the following types:
  • Recurrences
    • pay_rate
    • allowance
    • deduction
    • earning
    • employee_benefit
    • employer_benefit
    • reimbursement
  • Line Items
    • allowance_line_item
    • deduction_line_item
    • earning_line_item
    • employee_benefit_line_item
    • employer_benefit_line_item
    • reimbursement_line_item
  • Other
    • employee
    • contractor
    • work_assignment

Request

The body of the request must be an array of entity objects. These properties of the entity objects are documented in the API reference for each entity type, but in general the batch upsert endpoint takes the same properties with the same restrictions as the one-off create or update endpoints. If an object includes an id property, the existing entity with that ID will be updated. Otherwise, a new entity will be created. For example, this request updates one existing earning line item (with ID ernli_01J8KXC9R4MQVW2FXZN7Y5H3B8) and creates two new earning line items: POST /earning_line_items/batch/upsert
[
  {
    "id": "ernli_01J8KXC9R4MQVW2FXZN7Y5H3B8",
    "custom_amount": 500.0,
    "custom_hours": 5
  },
  {
    "pay_stub_id": "payst_01J8KXB4N6RQWM2FVZH9Y3T5C8",
    "earning_type": "wage",
    "title": "Tuesday Wages",
    "custom_amount": 300.0,
    "custom_hours": 3
  },
  {
    "pay_stub_id": "payst_01J8KXB4N6RQWM2FVZH9Y3T5C8",
    "earning_type": "wage",
    "title": "Wednesday Wages",
    "custom_amount": 400.0,
    "custom_hours": 4
  }
]

Response

On success, the operation returns 202 Accepted with an async_task handle. See Asynchronous Execution for how to track completion. ⚠️ Partners must treat any 200-level response as success. ⚠️ For example, the response to the upsert request above might look like:
{
  "id": "asnct_01KS0G8Z2YD3T9KQNFW1XEA7HB",
  "object": "async_task",
  "data": {
    "type": "batch_upsert",
    "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 — either when you poll links.self and see data.status change, or when the async_task_completed webhook arrives — data.results will contain one entry per request entity, in the same order. results[n] corresponds to data[n] in the request body, whether the entity was created or updated:
{
  "id": "asnct_01KS0G8Z2YD3T9KQNFW1XEA7HB",
  "object": "async_task",
  "data": {
    "type": "batch_upsert",
    "status": "completed",
    "completed_at": "2026-05-19T16:14:35Z",
    "results": [
      {
        "id": "ernli_01J8KXC9R4MQVW2FXZN7Y5H3B8",
        "object": "earning_line_item"
      },
      {
        "id": "ernli_01KBMZDVV9G7713DJRYP9RJFTP",
        "object": "earning_line_item"
      },
      {
        "id": "ernli_01KBMZE2SY5HWB4WWR9Q8960K8",
        "object": "earning_line_item"
      }
    ],
    "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. In order to identify which entity in the request the validation messages apply to, the validation message keys will be prefixed with data.n., where n is the zero-based index of the entity. For example, if the first earning entity in a request had an invalid business_preset_id value, the validation message key would be data.0.business_preset_id
  ...
  "data.0.business_preset_id": "The selected business_preset_id is invalid.",
  ...

Notes

  • If any entity fails validation, no entities will be created or updated.
  • When using a business_preset_id, properties must either be omitted or match the preset’s values.
  • If batch upserting line items, the payroll’s totals are automatically recalculated after the batch upsert.
  • If batch upserting line items, the payroll must be in draft status.

Batch Delete Line Items

Nmbr’s API allows you to delete multiple entities of a single type in a single API call.

Endpoint

POST /<type>s/batch/delete <type> can be one of the following types:
  • Recurrences
    • pay_rate
    • allowance
    • deduction
    • earning
    • employee_benefit
    • employer_benefit
    • reimbursement
  • Line Items
    • allowance_line_item
    • deduction_line_item
    • earning_line_item
    • employee_benefit_line_item
    • employer_benefit_line_item
    • reimbursement_line_item
  • Other
    • employee
    • contractor
    • work_assignment

Request

The body of the request must be an array of the IDs of the entities to delete. For example, this request deletes three earning line items: POST /earning_line_items/batch/delete
[
  "ernli_01J8KXC9R4MQVW2FXZN7Y5H3B8",
  "ernli_01KBMZDVV9G7713DJRYP9RJFTP",
  "ernli_01KBMZE2SY5HWB4WWR9Q8960K8"
]

Response

On success, the operation returns 202 Accepted with an async_task handle. See Asynchronous Execution for how to track completion. ⚠️ Partners must treat any 200-level response as success. ⚠️ For example, the response to the delete request above might look like:
{
  "id": "asnct_01KS0G8Z2YD3T9KQNFW1XEA7HB",
  "object": "async_task",
  "data": {
    "type": "batch_delete",
    "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 — either when you poll links.self and see data.status change, or when the async_task_completed webhook arrives — data.results will contain one entry per request ID, in the same order. results[n] corresponds to data[n] in the request body. Deleted entities are still surfaced by ID — the records soft-delete and remain queryable:
{
  "id": "asnct_01KS0G8Z2YD3T9KQNFW1XEA7HB",
  "object": "async_task",
  "data": {
    "type": "batch_delete",
    "status": "completed",
    "completed_at": "2026-05-19T16:14:35Z",
    "results": [
      {
        "id": "ernli_01J8KXC9R4MQVW2FXZN7Y5H3B8",
        "object": "earning_line_item"
      },
      {
        "id": "ernli_01KBMZDVV9G7713DJRYP9RJFTP",
        "object": "earning_line_item"
      },
      {
        "id": "ernli_01KBMZE2SY5HWB4WWR9Q8960K8",
        "object": "earning_line_item"
      }
    ],
    "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. In order to identify which entity in the request the validation messages apply to, the validation message keys will be prefixed with data.n., where n is the zero-based index of the entity. For example, if the first entity in a request couldn’t be deleted because it was a managed line item, the validation message key would be data.0.is_managed
  ...
  "data.0.is_managed": "The line item is managed and cannot be deleted.",
  ...

Notes

  • If any entity fails validation, no entities will be deleted.
  • If batch deleting line items, the line item must not be a managed line item.
  • If batch deleting line items, the payroll’s totals are automatically recalculated after the batch delete.
  • If batch deleting line items, the payroll must be in draft status.