Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.cadanapay.com/llms.txt

Use this file to discover all available pages before exploring further.

A payroll moves through a series of statuses from creation to completion. This page covers what happens at each stage, the webhook events you’ll receive, and how multi-currency payrolls are handled.

Overview

Only 3 API calls needed — everything after approval is automatic.
1

Create a payroll

Select the worker type (employee/contractor) and create a payroll. POST /v1/payrolls → Webhook: payroll.created
2

Save entries

Provide the pay date, pay period, and compensation for each worker. POST /v1/payrolls/{id}/save → Webhook: payroll.status.updated (saved)
3

Approve

Approve the payroll to trigger fund collection and scheduling. POST /v1/payrolls/{id}/approve → Webhook: payroll.status.updated (approved)
For the full integration walkthrough, see Pay Workers via Payroll.

What Happens After Approval

Once you call POST /v1/payrolls/{id}/approve, the rest is automatic:

Balance Check

Cadana checks whether the business account has sufficient funds to cover the payroll.
  • Sufficient funds — the payroll moves to Scheduled and is queued for the payroll date. Webhook: payroll.status.updated (scheduled)
  • Insufficient funds — the payroll moves to Awaiting Funds. If a bank account is connected, an ACH debit initiates automatically. Once the account is funded, the payroll proceeds — no need to re-approve.

Scheduling

If the payroll date is in the future, the payroll stays in Scheduled until that date. If the payroll date is today or in the past, disbursement begins immediately.

Disbursement

On the payroll date, Cadana triggers disbursements for each worker. The payroll moves to Processing. Each worker’s payment is handled independently — if one worker’s payment fails, the others continue. Webhook: payroll.status.updated (processing) Once the payroll reaches Processing, each entry on the GET /v1/payrolls/{payrollId} response includes a transactionIds array linking it to its underlying disbursement transactions. This is a polling-friendly alternative to the per-worker transaction.* webhook events for clients that prefer to fetch state on demand.

Completion

Once all disbursements are confirmed, the payroll moves to Completed. Webhook: payroll.status.updated (completed) After completion, the payroll response includes an invoiceId. Fetch the invoice for a detailed breakdown of fees charged for the payroll run.

Status Reference

These are the values returned in the status field on GET /v1/payrolls and GET /v1/payrolls/{id}.
StatusMeaning
CreatedEmpty payroll shell, no entries yet — returned right after POST /v1/payrolls
SavedEntries added, ready for approval
Pending ApprovalSubmitted for an approver to action. You will only see this when the dashboard is used to submit a payroll; the POST /v1/payrolls/{id}/approve endpoint accepts payrolls in either Saved or Pending Approval.
Awaiting FundsApproved but the business balance is insufficient — payroll proceeds automatically once funded
ScheduledFunded and queued for the payroll date
ProcessingDisbursements are being sent to workers
CompletedAll disbursements confirmed (terminal)
RejectedAn approver declined the payroll (terminal)
DeletedCaller deleted the payroll via DELETE /v1/payrolls/{id} before disbursement started (terminal)
POST /v1/payrolls/{id}/save returns 204 immediately, but the new Saved status takes a few seconds to be reflected on GET /v1/payrolls/{id}. A tight poll loop right after save may still see Created briefly. POST /v1/payrolls/{id}/approve is strongly consistent — a follow-up GET will reflect the new status immediately.
Saved and Awaiting Funds are not auto-cancelled — they sit indefinitely until you explicitly approve, delete, or (for Awaiting Funds) fund the business balance.

Deleting a Payroll

You can delete a payroll at any point before it starts processing. Returns 204 on success. The payroll moves to Deleted.
Once a payroll reaches Processing or Completed, it cannot be deleted.

Webhook Events

Cadana emits two payroll webhook event types. Subscribe via the Dashboard or see Webhooks for setup.

payroll.created

Fired when a new payroll is created.
JSON
{
  "id": "e13b9e14-c062-42ea-8563-8fc9223b29b5",
  "workerType": "CONTRACTOR",
  "type": "ONE_OFF",
  "tenantKey": "cad95193904"
}

payroll.status.updated

Fired when the payroll moves into one of the values below.
JSON
{
  "id": "e13b9e14-c062-42ea-8563-8fc9223b29b5",
  "status": "completed",
  "tenantKey": "cad95193904"
}
status valueMeaning
savedEntries saved, ready for approval
approvedPayroll approved
scheduledFunds collected, queued for the payroll date
processingDisbursements being sent to workers
completedAll disbursements confirmed
No webhook fires for the Awaiting Funds, Rejected, or Deleted states. Poll GET /v1/payrolls/{id} if you need to detect them.

Per-Worker Transaction Events

Each worker’s payment fires individual transaction events: transaction.initiated, transaction.succeeded, and transaction.failed. These let you track each worker’s payout independently of the overall payroll status. The transaction’s reference field contains the payrollId. recipientId is the worker’s identifier — use recipientType to interpret it: a userId when recipientType: "USER" (Cadana wallet payments), or a personId when EMPLOYEE or CONTRACTOR (direct bank payments).
JSON
{
  "id": "9af0f05e-1efa-407b-be23-8595f89a1b2a",
  "amount": { "currency": "USD", "amount": 100000 },
  "type": "PAYROLL",
  "reference": "a1b2c3d4-e5f6-7890-abcd-ef0123456789",
  "recipientId": "8ef9a712-cdae-4110-b1ea-9ba95abbee6e",
  "recipientType": "USER",
  "tenantKey": "cad95193904"
}
See Events for all event types and payload details.

Multi-Currency Payrolls

When a worker’s salary currency differs from the business’s funding currency (e.g., a US company paying a contractor in BRL), FX conversion is involved.

How It Works

1

FX rates captured at save time

Cadana captures FX rates and calculates the debit amount in the funding currency. Each entry stores its conversion rate individually. Pass ?includeFxRates=true on the save call to receive the captured rates in the response (200 OK with { "fxRates": { "INR-USD": 0.0120, ... } }) instead of polling the payroll.
2

Rates may refresh at disbursement time

If there’s a delay between save and disbursement (future payroll date, awaiting funds, etc.), rates may be refreshed with current market rates.
3

Debit amount may change

After a rate refresh, the total debit is recalculated. If the new amount exceeds the business balance, the payroll moves back to Awaiting Funds.
  1. Poll after save — check the debit amount on the payroll to understand the total cost in your funding currency.
  2. Fund with headroom — if your payroll date is days away, consider funding slightly above the quoted debit to absorb potential rate movement.
  3. Monitor Awaiting Funds — if a rate refresh causes the debit to exceed your balance, the payroll pauses until you top up.

Edge Cases

Individual Worker Payment Fails

If one worker’s disbursement fails (e.g., invalid bank details), the other workers’ payments continue. The payroll still moves to Completed once all disbursements are resolved. Use transaction.failed webhooks to identify which worker’s payment needs attention. The payroll itself does not have a failed status — that string only appears on per-worker transaction.* events.

Multiple Payrolls Awaiting Funds

When multiple payrolls are in Awaiting Funds and the business adds funds, Cadana schedules them in priority order:
  1. Earliest payroll date first
  2. Creation time as a tiebreaker
Only payrolls fully coverable by the available balance move to Scheduled. The rest remain in Awaiting Funds until more funds are added.

Payroll Stuck in Awaiting Funds

The payroll remains in Awaiting Funds indefinitely until the business account is funded. Once sufficient funds are available, the payroll proceeds automatically. See Fund Your Account for funding options.

Re-saving Before Approval

You can call save multiple times before approving. Each save replaces the previous entries entirely — there is no incremental add/remove of individual entries.

Next Steps

Pay Workers via Payroll

Step-by-step guide to create, save, and approve a payroll

Fund Your Account

Add funds via bank transfer, ACH, or crypto

Webhooks

Configure your webhook endpoint

Events

All event types and payload schemas