- Track A, your own company. You pay your own contractors or employees.
- Track B, client companies. Companies you onboard pay their own recipients.
In every path or body,
company_id is the company that pays: yours in Track A,
the client’s in Track B.
1. Onboard a client company
Track B only. A company you create is linked to your partner account. You receive its webhooks and can act on it with your API key.
Wait for
approved before you create payrolls. You also receive a
company.kyc_status_update webhook when the status changes.
2. Create and verify recipients
A recipient is someone the company pays: a contractor, an employee, or a vendor. You collect their details and documents, then send them through the API. Bitwage does not email the recipient, and they never sign in. Do this in order. Therecipient_id from the create response is the user_id
you send to the user endpoints.
Create the recipient
Sendrecipient_type of contractor or employee, and a worker object with
their name, email, phone, date of birth, and address. Set external_id to
your own ID for them. It must be unique within the company. A duplicate
returns recipient-external-id-already-used.
recipient_id is the user you upload documents for. Keep it.
Create each person with this endpoint. POST /api/user/create makes a user
who is not linked to a company, and a later recipients/create with the same
email fails with recipient-worker-email-already-exists.
For a vendor, send recipient_type vendor and a vendor object instead of
worker. The returned recipient_id is a company ID.
Upload documents
CallPOST /api/user/document/upload once per document, with user_id set to
the recipient_id.
A failed image upload returns
user_update_advanced_data_failed. These
endpoints, along with POST /api/user/update and POST /api/user/kyc-initiate,
use the legacy error body and do not take
an idempotency key. Check GET /api/user/document/list before you upload the
same document again.
Start verification
After the documents are uploaded, callPOST /api/user/kyc-initiate with the
user_id. Bitwage sends user.kyc_status_update when the status changes. You
can also read kyc_verification_status from GET /api/user/detail. It is
pending, approved, rejected, unapproved, or neutral (manual review).
Some paying companies require every recipient to be approved before a
payroll is accepted. In that case payroll creation returns a 422 saying the
recipients must have approved KYC.
3. Add payment destinations
A distribution is where a recipient is paid: a crypto address or a bank account. A recipient can have several, and each payment is split across them by percentage (the allocation).- For a bank account, call
GET /api/user/distribution/fieldsand send exactly those fields indistribution_data. - Create the distribution. The response includes its current
allocation. - Call the allocations endpoint so the allocations of the recipient’s
distributions add up to
100.
4. Pay recipients
- Optionally estimate fees. For a balance account funding method, pass the
returned quote
idasquote_idwhen you create the payroll to lock the rate. - Create the payroll with a
to_payitem for each recipient. Each item has eitheruser_idorcompany_id. - Always send
funding_methodand anexternal_id. Partners can’t set a company default, so a request that omitsfunding_methodfails with a422. - Keep the
payroll_idfrom the response. This response is the only creation signal. There is no creation webhook. - Fund the payroll as the funding method describes. If a crypto quote expires first, refresh it.
Pay vendors
Vendor payments use quotes, invoice uploads, and a separate create call.
These endpoints support API key authentication only.
5. Track the payroll
You can follow a payroll by webhook, by polling, or both.
Webhooks are sent once and aren’t retried. Use them for speed and the GET
endpoints as the source of truth. See Webhooks for the
payloads, ordering, and signature validation.
Production checklist
- Send an
Idempotency-Keyon every endpoint marked Yes or Required. See Idempotency. - Store
payroll_id, yourexternal_id, and eachsubpayroll_id. - Branch on the error
code, not onmessage. See Errors. - Verify the
x-bitwage-signatureheader on every webhook. - Reconcile open payrolls with
GET /api/company/{company_id}/payrollson a schedule, in case a webhook was missed.