Skip to main content
This is the accounts-payable workflow end to end: set up the vendor and how they get paid, record the bill, pay it from one of your payment methods on file, and track the money until it arrives. For the objects involved and the payment lifecycle, see How Bill Payments Work.
Before you start: You need a sandbox API key (see the Quickstart) and at least one funding source on file — the bank accounts and cards you’ve connected in the Nickel dashboard. Step 4 shows where to add one. Amounts are in cents throughout.
1

Create the vendor

A vendor is who you pay. Create one with POST /vendor:
curl -X POST https://rest.staging.nickel.com/vendor \
  -H "Authorization: Bearer $NICKEL_SANDBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Brooklyn Paper Supply",
    "emails": ["billing@brooklynpapersupply.com"],
    "type": "BUSINESS",
    "address": {"street1": "412 Atlantic Ave", "city": "Brooklyn", "state": "NY", "zip": "11217", "country": "US"}
  }'
{
  "id": "cmra1j4un00013u02spto0xz7",
  "name": "Brooklyn Paper Supply",
  "emails": ["billing@brooklynpapersupply.com"],
  "address": "412 Atlantic Ave",
  "city": "Brooklyn",
  "state": "NY",
  "zip": "11217",
  "type": "BUSINESS",
  "vendorDeliveryMethods": []
}
Pass "syncToQbo": true to also create the vendor in QuickBooks Online if your account has it connected — see QuickBooks.
2

Add a delivery method

A delivery method is how the vendor receives funds. Set it yourself if you have their details — or better, let the vendor enter their own bank details so you never handle them.
curl -X PUT https://rest.staging.nickel.com/vendor/cmra1j4un00013u02spto0xz7/deliveryMethod/ach \
  -H "Authorization: Bearer $NICKEL_SANDBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{"routingNumber": "101050001", "accountNumber": "5550123456", "accountType": "checking"}'
These endpoints return only {"vendorId": "..."} — not the delivery method. Fetch the vendor to get the vendorDeliveryMethods[].id you’ll need when paying:
curl https://rest.staging.nickel.com/vendor/cmra1j4un00013u02spto0xz7 \
  -H "Authorization: Bearer $NICKEL_SANDBOX_KEY"
{
  "id": "cmra1j4un00013u02spto0xz7",
  "name": "Brooklyn Paper Supply",
  "vendorDeliveryMethods": [
    {
      "id": "cmra1jcxw00033u02rdotftsr",
      "type": "ACH",
      "routingNumber": "101050001",
      "accountType": "checking",
      "bankName": "First Bank of the United States",
      ...
    }
  ],
  ...
}
3

Create the bill

Record what you owe with POST /bill:
curl -X POST https://rest.staging.nickel.com/bill \
  -H "Authorization: Bearer $NICKEL_SANDBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "vendorId": "cmra1j4un00013u02spto0xz7",
    "amountCents": 3500,
    "dueDateEpochMillis": 1785542400000,
    "reason": "August paper delivery",
    "billNumber": "BPS-1042"
  }'
Bill dates are epoch milliseconds (1785542400000 is 2026-08-01), unlike invoices which use YYYY-MM-DD strings. Mixing them up creates bills due in 1970.
{
  "id": "cmra1lpku000111021gzwnia4",
  "amountDueInCents": 3500,
  "totalAmountInCents": 3500,
  "vendorId": "cmra1j4un00013u02spto0xz7",
  "vendorName": "Brooklyn Paper Supply",
  "dueDate": "2026-08-01T00:00:00.000Z",
  "reference": "BPS-1042",
  "status": "Open",
  "documents": []
}
Note the naming shift: the billNumber you send comes back as reference. A new bill is Open and flips to Paid once a payment covers it — or as soon as one is scheduled (see step 5). The scheduledWithdrawalDate field distinguishes the two: non-null means scheduled, not settled.
4

Pick your funding source

A funding source is one of your own payment methods — the bank account or card the bill is paid from. Funding sources are added in the dashboard, not through the API: in the Nickel dashboard, go to Settings → Bill Pay and click Add new, then either connect a bank account (Nickel walks you through linking it with Plaid) or enter a credit card.Once added, list the payment methods your organization can pay from with GET /billPaymentMethods:
{
  "billPaymentMethods": [
    {
      "id": "cmp0x7q6p002ntf02cktmo34p",
      "type": "ACH",
      "achDetails": {
        "bankName": "EXAMPLE BANK",
        "routingNumber": "110000000",
        "accountNumberLastFour": "7890"
      },
      ...
    }
  ]
}
The id is the merchantPaymentMethodId for the next step.
5

Pay it

POST /bill/pay is a batch: each item names the bill, the vendor, your funding source, the vendor’s delivery method, and who to notify:
curl -X POST https://rest.staging.nickel.com/bill/pay \
  -H "Authorization: Bearer $NICKEL_SANDBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "payments": [
      {
        "billId": "cmra1lpku000111021gzwnia4",
        "vendorId": "cmra1j4un00013u02spto0xz7",
        "merchantPaymentMethodId": "cmp0x7q6p002ntf02cktmo34p",
        "vendorDeliveryMethodId": "cmra1jcxw00033u02rdotftsr",
        "notificationEmails": ["ap@yourcompany.com"],
        "vendorMemo": "Invoice BPS-1042"
      }
    ],
    "idempotencyKey": "bill-run-2026-07-06"
  }'
Always send an idempotencyKey. If the request times out and you retry with the same key, Nickel won’t pay the same bills twice.
{
  "billPayments": [
    {
      "id": "cmra1lqiq00051102m6o0dpiq",
      "status": "PENDING",
      "amountInCents": 3500,
      "createdAt": 1783392088706,
      "vendorDeliveryMethodId": "cmra1jcxw00033u02rdotftsr",
      "billPayableId": "cmra1lpuq00031102ol4yf1a1"
    }
  ],
  "billErrors": [],
  "scheduled": []
}
Two things to handle in the response:
  • Per-bill failures land in billErrors[] ({billId, error}) while valid items still go through — check it on every call. Bills that are already paid, or that already have a pending scheduled payment, are reported here too (they no longer fail the whole batch) and are omitted from billPayments[] so any pre-existing payments on those bills aren’t surfaced as if this batch created them.
  • Batch-level problems — like malformed input — fail the whole request with a top-level {"error": "..."} instead.
Scheduling: add withdrawalDateEpochMillis (when to debit you) or arrivalDateEpochMillis (when it should reach the vendor — Nickel back-computes the withdrawal date from the payment rail’s delivery time; dates too soon for the rail are rejected). A scheduled item creates no bill payment until the withdrawal day — instead the response acknowledges it in scheduled:
{
  "billPayments": [],
  "billErrors": [],
  "scheduled": [{ "billId": "cmra1lpku000111021gzwnia4", "withdrawalDate": "2026-07-10" }]
}
While it’s pending, GET /bill/{billId} reports the day in scheduledWithdrawalDate (and the bill already reads status: "Paid", even though nothing has moved yet). No webhook fires at scheduling time. On the withdrawal day the payment executes and rejoins the flow below — a real bill payment appears and bill_payment.succeeded or bill_payment.failed fires. A failed scheduled payment is not retried: the bill returns to Open and you submit a new POST /bill/pay. See Scheduled payments for the full contract.
6

Track it

Poll GET /billPayment/{billPaymentId} or subscribe to webhooks: bill_payment.succeeded, bill_payment.failed, and bill_payment.cancelled, plus the check lifecycle (check.sentcheck.deliveredcheck.deposited, with check.returned / check.failed for problems) when paying by mailed check. An ACH bill payment sits in PENDING while the debit and transfer run their course — see the lifecycle.
7

Cancel if you need to

A payment that hasn’t been sent to the payment network yet can be stopped with POST /billPayment/{billPaymentId}/cancel:
curl -X POST https://rest.staging.nickel.com/billPayment/cmra1lqiq00051102m6o0dpiq/cancel \
  -H "Authorization: Bearer $NICKEL_SANDBOX_KEY"
{
  "id": "cmra1lqiq00051102m6o0dpiq",
  "status": "FAILED",
  "amountInCents": 3500,
  ...
}
A cancelled payment currently reports status: "FAILED" — the same terminal status as a genuine failure. Use the bill_payment.cancelled webhook (or the fact that you called cancel) to tell the two apart.
A scheduled payment has no bill payment to cancel yet — cancel it through its bill instead with POST /bill/{billId}/cancelScheduledPayment:
curl -X POST https://rest.staging.nickel.com/bill/cmra1lpku000111021gzwnia4/cancelScheduledPayment \
  -H "Authorization: Bearer $NICKEL_SANDBOX_KEY"
The response is the refreshed bill — status back to Open, scheduledWithdrawalDate back to null. A 404 means there’s no pending scheduled payment to cancel: it never existed, it already executed, or it was already cancelled (cancellation emits no webhook, so check GET /bill/{billId} when in doubt).
Bills and bill payments you create through the API are never written to QuickBooks. Paying a bill that was imported from QuickBooks is — see QuickBooks for the full table.

Where to go next

Collect vendor bank details

Let vendors enter their own details on a secure form.

Receive webhooks

Hear about bill_payment and check events as they happen.

How bill payments work

The objects and lifecycle behind this guide.

QuickBooks

What does and doesn’t appear in your books.