Keep data in sync

Where employers can update payroll information in your product and in PayrollKit, it's important for records to stay aligned.

Send changes through the API. When PayrollKit changes a supported setup, payroll or output record, including through the embedded component, a signed webhook tells you which record to retrieve.

The retrieved record is the current state. The webhook is only a prompt to fetch it.

Some tips before you start

  • Use your server-side PayrollKit access token to authenticate all API requests.
  • Include an Idempotency-Key with every request that creates or changes data. Use a new key for each intended change and reuse it only when retrying that exact request.
  • Store each PayrollKit ID beside the matching ID in your product, together with its latest ETag when provided.

End to end guide

Send changes made in your product

When a record changes in your product, update the matching PayrollKit record using its stored ID. If the endpoint requires If-Match, send the latest ETag.

This example adds an employee's leaving date:

curl --request PATCH \
  --url https://api.payrollkit.co/v1/employers/er_7d91f2c8/employments/employment_4b72e190 \
  --header 'Authorization: Bearer YOUR_SERVER_ACCESS_TOKEN' \
  --header 'Content-Type: application/merge-patch+json' \
  --header 'If-Match: "rev_5a72e1c9"' \
  --header 'Idempotency-Key: employment:employment_4b72e190:leaver:v1' \
  --data '{ "endDate": "2026-08-21" }'

Save the returned record and ETag. If PayrollKit returns 409, retrieve the record again before deciding whether to apply your change.

Receive changes made in PayrollKit

Register one HTTPS endpoint with POST /webhook-endpoints and subscribe to resource.changed. Store the signing secret securely. PayrollKit returns it only in the original registration response and an exact replay using the original idempotency key; List webhook endpoints and a new-key attempt to register the same URL do not return it.

For each notification:

  1. Read Webhook-Timestamp as Unix seconds and reject it if it differs from the current time by more than five minutes.
  2. Verify Webhook-Signature against ${timestamp}.${rawBody} using the exact raw request body and HMAC-SHA256. Encode the digest as lowercase hexadecimal and compare the resulting v1,<digest> value using a constant-time comparison.
  3. Check that Webhook-Id matches deliveryId.
  4. Save the notification somewhere that survives a restart before returning 204, which must happen within 10 seconds. If you have already saved it, return 204 without saving it again.

Retrieve resourceUrl using your server access token, then save the returned record and ETag when provided. Only follow URLs on PayrollKit's API domain. Notifications may be duplicated or arrive out of order, so treat the retrieved record as the current state.

Recover from interruptions

If an update times out, retry the exact request with the same idempotency key. If you receive a conflict, retrieve the current record before deciding what to send next.

A timeout, network failure or response other than 204 triggers the documented retry schedule. Retries keep the same body and deliveryId but use a new timestamp and signature. PayrollKit does not provide a webhook replay feed. After a longer endpoint outage, list or retrieve the current PayrollKit records your product mirrors instead of assuming it received every change notice.

If a local PayrollKit ID is missing, use the matching partner...Id filter to find it again.