> ## 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.

# Payroll Lifecycle

> Stages in the lifecycle of a Payroll

Nmbr will automatically generate payrolls and pay stubs for configured pay schedules and employment profiles.

The next payroll in the pay schedule, ordered by `pay_date`, will automatically have all recurring earnings, allowances, reimbursements, deductions and benefits populated on its Pay Stubs.

## Interacting with payrolls

You can retrieve a list of payrolls from the payroll API endpoint:

**Request**

```bash theme={null}
curl --request GET \
     --url https://sandbox.nmbr.co/services/payroll/payrolls \
     --header 'Authorization: Bearer <access_token>' \
     --header 'accept: application/json'
```

You can retrieve an individual payroll by querying for a specific ID:

**Request**

```bash theme={null}
curl --request GET \
     --url https://sandbox.nmbr.co/services/payroll/payrolls/<payroll_id> \
     --header 'Authorization: Bearer <access_token>' \
     --header 'accept: application/json'
```

**Response**

```json theme={null}
{
  "id": "01hfvzcp9c5phwfjkcra40rsg4",
  "object": "payroll",
  "data": {
    "type": "regular",
    "period_start": "2023-01-01T00:00:00.000000Z",
    "period_end": "2023-01-15T00:00:00.000000Z",
    "pay_date": "2023-01-13T00:00:00.000000Z",
    "employee_summary": {
      "gross": "1161.54",
      "deductions": null,
      "reimbursements": null,
      "net": "712.31"
    },
    "contractor_summary": {
      "gross": "100.00",
      "reimbursements": "15.00",
      "net": "115.00"
    },
    "company_summary": {
      "taxes_and_contributions": "73.56",
      "benefits": "50.00",
      "total": "123.56"
    },
    "liability": "1150.10",
    "cash_requirement": "1150.10",
    "status": "draft",
    "approved_at": null,
    "created_at": "2023-11-22T16:56:38.000000Z",
    "updated_at": "2023-11-22T16:56:38.000000Z"
  },
  "links": {
    "self": "https://sandbox.nmbr.co/services/payroll/payrolls/01hfvzcp9c5phwfjkcra40rsg4"
  }
}
```

In order to interact with employees earnings and deductions for a given pay period, you will need to retrieve a list of Pay Stubs for a given payroll.

Query the pay stub API endpoint to retrieve a list of pay stubs for a specific payroll id:

**Request**

```bash theme={null}
curl --request GET \
     --url 'https://sandbox.nmbr.co/services/payroll/pay_stubs?payroll_id=<payroll_id>' \
     --header 'Authorization: Bearer <access_token>' \
     --header 'accept: application/json' \
```

**Response**

```json theme={null}
{
  "id": "01hfwg2784vc1408yhmjp4x81q",
  "object": "pay_stub",
  "data": {
    "has_taxes_calculated": true,
    "employee_gross": "2520",
    "employee_subtractions": "574.91",
    "employee_reimbursements": "10",
    "employee_net": "1815.09",
    "earning_line_items": {
      "object": "list",
      "data": [
        {
          "id": "01hfwg27gtha4m82def3y3spz4",
          "object": "earning_line_item",
          "data": {
            "amount": 2500,
            "line_item_type": "earning",
            "earning": {
              "earning_type": "salary",
              "hours": null,
              "accrued_vacation_pay": null
            },
            "created_at": "2023-11-22T21:48:01.000000Z",
            "updated_at": "2023-11-22T21:48:01.000000Z"
          }
        }
      ]
    },
    "statutory_withholding_line_items": {
      "object": "list",
      "data": [
        {
          "id": "01hfye1vw8wqg7a4x4nwrfmt17",
          "object": "statutory_withholding_line_item",
          "data": {
            "line_item_type": "statutory_withholding",
            "amount": 134.6,
            "statutory_withholding": {
              "statutory_withholding_type": "provincial_income_tax",
              "hours": null
            },
            "created_at": "2023-11-23T15:51:21.000000Z",
            "updated_at": "2023-11-23T15:51:21.000000Z"
          }
        }
      ]
    },
    "allowance_line_items": {
      "object": "list",
      "data": [
        {
          "id": "01hfycrt5cr7vajwaej1yre9q5",
          "object": "allowance_line_item",
          "data": {
            "line_item_type": "allowance",
            "amount": 10,
            "allowance": {
              "allowance_type": "automobile_and_motor_vehicle",
              "recurring_allowance": null
            },
            "created_at": "2023-11-23T15:28:56.000000Z",
            "updated_at": "2023-11-23T15:28:56.000000Z"
          }
        }
      ]
    },
    "reimbursement_line_items": {
      "object": "list",
      "data": [
        {
          "id": "01hfycs5ze4hs2c5erjb6jx4kn",
          "object": "reimbursement_line_item",
          "data": {
            "line_item_type": "reimbursement",
            "amount": 10,
            "reimbursement": {
              "reimbursement_type": "non_taxable_reimbursement",
              "recurring_reimbursement": null
            },
            "created_at": "2023-11-23T15:29:08.000000Z",
            "updated_at": "2023-11-23T15:29:08.000000Z"
          }
        }
      ]
    },
    "benefit_line_items": {
      "object": "list",
      "data": [
        {
          "id": "01hfycn0yy7gs7s5ek2k6j854w",
          "object": "benefit_line_item",
          "data": {
            "line_item_type": "benefit",
            "amount": 30,
            "benefit": {
              "employee_benefit": null,
              "benefit_type": "dental",
              "company_contribution_amount": 10,
              "employee_contribution_amount": 30
            },
            "created_at": "2023-11-23T15:26:52.000000Z",
            "updated_at": "2023-11-23T15:26:52.000000Z"
          }
        }
      ]
    },
    "post_tax_deduction_line_items": {
      "object": "list",
      "data": [
        {
          "id": "01hfycw58xh8pp8j9agw63esgv",
          "object": "post_tax_deduction_line_item",
          "data": {
            "line_item_type": "post_tax_deduction",
            "amount": 10,
            "post_tax_deduction": {
              "post_tax_deduction_type": "income_tax",
              "recurring_post_tax_deduction": null
            },
            "created_at": "2023-11-23T15:30:46.000000Z",
            "updated_at": "2023-11-23T15:30:46.000000Z"
          }
        }
      ]
    },
    "created_at": "2023-11-22T21:48:01.000000Z",
    "updated_at": "2023-11-23T15:51:21.000000Z"
  },
  "links": {
    "self": "https://sandbox.nmbr.co/services/payroll/pay_stubs/01hfwg2784vc1408yhmjp4x81q"
  }
}
```

### Filtering the payrolls list

`GET /payrolls` requires either a `business_entity_id` or a `pay_schedule_id`. Without one of them, the request returns a `422`.

The `status` filter accepts one or more comma-separated values. Pass several to retrieve everything in flight in a single call:

**Request**

```bash theme={null}
curl --request GET \
     --url 'https://sandbox.nmbr.co/services/payroll/payrolls?business_entity_id=<business_entity_id>&status=draft,approved,processing' \
     --header 'Authorization: Bearer <access_token>' \
     --header 'accept: application/json'
```

Invalid status values are rejected with a `422`.

To retrieve every payroll except certain statuses, use `not_status` with the same comma-separated form:

**Request**

```bash theme={null}
curl --request GET \
     --url 'https://sandbox.nmbr.co/services/payroll/payrolls?business_entity_id=<business_entity_id>&not_status=draft,paid' \
     --header 'Authorization: Bearer <access_token>' \
     --header 'accept: application/json'
```

Unknown values passed to `not_status` are ignored rather than rejected, so check them against the documented statuses.

### Retrieving the upcoming regular draft payroll

To retrieve the upcoming regular draft payroll — the next payroll that is ready for editing — filter the payrolls index by `type=regular` and `status=draft`, and sort by `pay_date`:

**Request**

```bash theme={null}
curl --request GET \
     --url 'https://sandbox.nmbr.co/services/payroll/payrolls?business_entity_id=<business_entity_id>&type=regular&status=draft&sort=pay_date:asc' \
     --header 'Authorization: Bearer <access_token>' \
     --header 'accept: application/json'
```

The first payroll in the response is the upcoming regular draft payroll.

The `type=regular` filter returns regular payrolls only. Off-cycle and historical payrolls are excluded, so query them separately if you need them.

> 📘 Why not use `next_payrolls`?
> The `next_payrolls=only` filter returns the current payroll being handled by Nmbr for each Pay Schedule — which may be an `approved`, `processing`, or `failed` payroll, not necessarily one that is ready for editing.
> Filtering by `type=regular&status=draft` and sorting by `pay_date` ensures you always get the next editable regular payroll.

You can update an individual payroll line item using the API. The example below could be used to enter 10 hours for the given pay rate.

```bash theme={null}
curl --request PUT \
     --url https://sandbox.nmbr.co/services/payroll/earnings/<earning_id> \
     --header 'Authorization: Bearer <access_token>' \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '{
                "id": "01hc2p95nqdbz1ss6b90yf8zjv",
                "earning_type": "hourly",
                "amount": 100,
                "hours": 10,
                "accrued_vacation_pay": 2
            }'
```

## Approving payroll

When a payroll is approved (via the `/payrolls/<payroll_id>/approve` endpoint), its status changes to approved. Once approved, the payroll can no longer be modified. This is because the taxes, net pay, and benefits have been calculated and saved for the payroll.

```bash theme={null}
curl --request POST \
     --url https://sandbox.nmbr.co/services/payroll/payrolls/<payroll_id>/approve \
     --header 'Authorization: Bearer <access_token>' \
     --header 'accept: application/json'
```

> 📘 Rejected Approvals
> If a payroll is in an invalid state when the `/approve` endpoint is called, the approval will fail, returning a `422 Unprocessable Entity` response.
> Common reasons for failure include:
>
> * The payroll is past its approval date.
> * A pay stub has a negative net amount.
> * The payroll is not in the `draft` status.

Once a payroll is approved, Nmbr will automatically collect funds from the employer's bank account, make sure wages are transferred into employees' bank accounts on payday, and remit tax payments on time. We handle all of this behind the scenes, but you will still want to keep tabs on your payroll as it moves through each phase of its lifecycle.

Nmbr surfaces status information on the payroll objects returned by our API through the status field. A payroll can have one of five statuses: draft, approved, processing, paid, and failed. Below is an overview of these statuses, what they mean, and how payrolls transition between them.

> 📘 Payroll status webhooks
> When a payroll makes status transitions from approved to processing and from processing to paid, you can receive webhook notifications from our platform. See the webhook documentation for more details about how to set this up.

## Payroll Status Lifecycle

### draft

When a payroll is first created, it will be in the draft status. You can modify a draft payroll in any way you see fit. Payments to employees can be added and removed, earnings and hours worked can be modified on individual payroll items, etc. You can only delete a payroll while it's in draft.

### approved

When a payroll is approved (via the `/payrolls/<payroll_id>/approve` endpoint), its status changes to approved. Once approved, the payroll can no longer be modified. This is because the taxes, net pay, and benefits have been calculated and saved for the payroll.

If you determine that an error exists in the payroll, you can move it back to the draft status (via the `/payrolls/<payroll_id>/unapprove` endpoint). Once back in draft, the payroll can again be modified, previewed, and even deleted. If and when it is approved again, the payroll moves back into the approved status.

### processing

At the end of each business day at 5pm PT, our system collects all pending payrolls that have an approval deadline on that day and begins the multi-step process of moving money through the banking system to pay them out. The first step in this process is to submit an ACH transaction to debit the cash requirement for a payroll from the employer’s bank account. When a payroll’s debit transaction has been sent to the ACH network, it moves into a new status: processing.

Once a payroll is processing, it can no longer be moved back to draft. If you require a change to a processing payroll, reach out to us directly.

### paid

Due to the nature of the ACH network, the employer debit transaction will take 3 business days to complete. For example, if you approve a payroll with a Friday payday on Monday, we should have the employer’s funds in our bank account by Thursday morning. Once Nmbr has the employer’s cash requirement, we submit ACH transactions to send net pay to each employee who is set up for direct deposit. When these transactions complete, the payroll enters its final status: paid. Once here, the employees' payments for your payroll have been sent out . The employees are happy, and employers can focus on their business!

### partially\_paid

**Deprecated.** No payroll reaches this status today, and you should not filter on it. It remains a valid value only for backwards compatibility.

When an ACH payment to an employee or a contractor is returned (most often from an invalid account or routing number, or a frozen bank account), Nmbr marks that individual payment as failed and leaves the parent payroll in `processing`. There is no payroll-level "partially paid" signal. To find failures, check the child payment records for a `failed` status, then reach out to us directly to resolve one.

### failed

Just as we will unfortunately see times that an employee's payment will fail, there will also be times when the ACH transaction to debit the cash requirement for an employer's payroll will be returned. One common reason this occurs is because there are insufficient funds for the transaction in an employer's bank account at the time of request. When this happens, Nmbr sets the payroll's status to be failed and, if possible, pauses in-flight employee and contractor payments for that payroll. Our team then works with you to quickly wire the cash requirement for the failed payroll before unpausing and delivering the payroll's employee and contractor payments.
