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

# Write a Payroll Deduction

> Write a benefits deduction into the customer's payroll system.

A benefits deduction reaches payroll as a line item: an ICHRA allowance, an HSA contribution, a voluntary premium. It goes through the employee payroll run endpoint, which also carries earnings and taxes.

<Info>
  **Before you start**

  * You've confirmed the integration supports this write - see [Check write support](/guides/reading-writing/writing-data/meta-apis#supported-operations).
  * You can generate a stable idempotency key per deduction - see [Idempotency](/guides/reading-writing/writing-data/idempotency).
</Info>

## Steps

<Steps>
  <Step title="Fetch the schema">
    ```bash theme={null}
    curl --request GET \
      --url 'https://api.bindbee.dev/api/hris/v1/employee-payroll-runs/meta/post' \
      --header 'Authorization: Bearer <BINDBEE_API_KEY>' \
      --header 'X-Connector-Token: <CONNECTOR_TOKEN>'
    ```

    The response defines `deductions` as an array, with its own `required` array under `items`. It also says whether this connector expects `integration_params` - see [Integration-specific fields](/guides/reading-writing/writing-data/meta-apis#integration-specific-fields).

    **Result:** The contract for this integration.
  </Step>

  <Step title="Read the customer's payroll codes">
    ```bash theme={null}
    curl -G https://api.bindbee.dev/api/hris/v1/payroll-codes \
      --header 'Authorization: Bearer <BINDBEE_API_KEY>' \
      --header 'X-Connector-Token: <CONNECTOR_TOKEN>' \
      --data-urlencode 'page_size=200'
    ```

    Each code carries `code`, `name`, and a `type` of `EARNING` or `DEDUCTION`. Filter to the deduction codes and use `code` as the value the write expects.

    Which code belongs to which benefit plan is the customer's decision, so confirm the mapping with them and store it against the connector. An employer benefit also carries `payroll_codes` and `deduction_code`, which link a deduction line back to its plan by ID - see [Reading benefits](/guides/data-models/benefits).

    **Result:** Codes that exist in this customer's payroll configuration.
  </Step>

  <Step title="Build the body from the schema">
    The deduction body varies by provider. Workday takes a `type` and an `id`, ADP takes a `code`, and both take an `amount`. Build from the schema step 1 returned, and check the worked payload for the customer's platform on [Create Employee Payroll Run](/hris/employee-payroll-runs/create-employee-payroll-runs).

    Provider constraints live there too. Workday accepts one deduction per request and rejects a body carrying both earnings and deductions.

    Send amounts in the precision the schema specifies.

    **Result:** A body matching the contract for this platform.
  </Step>

  <Step title="Send with an idempotency key">
    ```bash theme={null}
    curl --request POST \
      --url 'https://api.bindbee.dev/api/hris/v1/employee-payroll-runs' \
      --header 'Authorization: Bearer <BINDBEE_API_KEY>' \
      --header 'X-Connector-Token: <CONNECTOR_TOKEN>' \
      --header 'X-Idempotency-Key: <UNIQUE_KEY>' \
      --header 'Content-Type: application/json' \
      --data @deduction.json
    ```

    Derive the key from the employee and the pay period, such as `payroll-{employee_id}-{period_end}`, so a retry reproduces it - see [Idempotency](/guides/reading-writing/writing-data/idempotency).

    **Result:** The deduction is written.
  </Step>

  <Step title="Verify the period it landed in">
    Read the run back and confirm the period - see [Get Employee Payroll Runs](/hris/employee-payroll-runs/get-employee-payroll-runs). The run carries `start_date`, `end_date` and `check_date`.

    Payroll systems close periods, and a write submitted after close can be applied to the next open one. The customer's cut-off is not exposed through the API, so treat a late change as an adjustment in the following period.

    **Result:** Confirmation the amount sits in the period you intended.
  </Step>
</Steps>

## Frequently Asked Questions

<AccordionGroup>
  <Accordion title="A deduction code is rejected">
    The code is absent from this customer's configuration, or belongs to a different pay group. Read [payroll codes](/hris/payroll-codes/get-payroll-codes) for the connector and match against `code`, rather than reusing a value from another customer.
  </Accordion>

  <Accordion title="A deduction was applied twice">
    The retry carried a different `X-Idempotency-Key`, so Bindbee read it as a new write - see [Idempotency](/guides/reading-writing/writing-data/idempotency). Correct the amount in the payroll system, then derive the key so the same deduction always produces the same value.
  </Accordion>

  <Accordion title="You need to map plan years to payroll periods">
    A payroll run carries `start_date`, `end_date` and `check_date`, and no plan year. Cross-reference the check date against the benefit's coverage period - see [Reading benefits](/guides/data-models/benefits).
  </Accordion>
</AccordionGroup>

## Related

* [Create Employee Payroll Run](/hris/employee-payroll-runs/create-employee-payroll-runs) - the endpoint, and the worked payload per provider
* [Get Create Employee Payroll Run Meta](/hris/employee-payroll-runs/get-create-employee-payroll-run-meta) - the schema endpoint step 1 fetches
* [Get Payroll Codes](/hris/payroll-codes/get-payroll-codes) - the customer's own earning and deduction codes
