Skip to main content
Webhooks tell your application that something happened — a payment landed, a payout went out, a bill payment failed — without you polling for changes. This guide sets up a working receiver. For the full event catalog, payload reference, and delivery/retry semantics, see Webhook Events.
Before you start: You need an HTTPS endpoint that Nickel can reach. For sandbox testing, a webhook.site inbox or an ngrok tunnel to your laptop works great.
1

Register your endpoint

Create the subscription with POST /webhook:
curl -X POST https://rest.staging.nickel.com/webhook \
  -H "Authorization: Bearer $NICKEL_SANDBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/webhooks/nickel/f3f9a2c81b7d"}'
{
  "id": "cmra19w22001msf02nuk4wo9m",
  "url": "https://example.com/webhooks/nickel/f3f9a2c81b7d",
  "secret": "whsec_your_webhook_signing_secret"
}
Store the secret alongside your API key — Nickel signs every delivery to this webhook with it, and your receiver uses it to tell real deliveries from forgeries in the next step.
Your account has one API webhook subscription — creating a webhook replaces the previous API webhook, but a legacy payment-alert webhook configured from the dashboard settings page is separate and stays put. Only a webhook registered via POST /webhook receives the events documented here.
2

Confirm the subscription

GET /webhook shows what’s registered — after the call above, exactly one entry (you can also re-read the secret here if you lose it):
{
  "webhooks": [
    {
      "id": "cmra19w22001msf02nuk4wo9m",
      "url": "https://example.com/webhooks/nickel/f3f9a2c81b7d",
      "secret": "whsec_your_webhook_signing_secret"
    }
  ]
}
"secret": null means the subscription predates webhook signing and its deliveries are unsigned — recreate it with POST /webhook to get a secret.
3

Handle deliveries

Every event arrives as a POST with the same thin envelope. Here’s a real delivery:
{
  "id": "097678b2-3161-4622-b226-8a7eac83b7fa",
  "created_at": "2026-07-07T02:34:36.498Z",
  "associated_object_type": "payment",
  "associated_object_id": "cmra1cu1h0020sf02v55com2p",
  "category": "payment.made",
  "type": "event"
}
Your receiver only needs to do five things — verify the signature, acknowledge fast, process later, deduplicate, and tolerate categories it doesn’t know:
app.post('/webhooks/nickel/f3f9a2c81b7d', (req, res) => {
  // verify the Nickel-Signature header against the raw body with your whsec_ secret
  if (!verifyNickelSignature(req.headers['nickel-signature'], req.rawBody, WEBHOOK_SECRET)) {
    return res.sendStatus(400) // not from Nickel
  }
  res.sendStatus(200) // acknowledge immediately — retries kick in after non-2xx

  const event = req.body
  if (alreadyProcessed(event.id)) return // deliveries can repeat; dedupe by event id

  switch (event.category) {
    case 'payment.made':
    case 'payment.failed':
      queueJob('syncPayment', event.associated_object_id)
      break
    // handle the categories you care about...
    default:
      break // ignore unknown categories — new ones may be added
  }
})
The verifyNickelSignature implementation — an HMAC-SHA256 over the timestamp and raw request body (capture it before your framework parses the JSON) — is in Verifying Signatures.Nickel retries failed deliveries with exponential backoff for up to 5 attempts, so slow handlers cause duplicate deliveries — do real work on a queue, not in the request.
4

Fetch the real state

The event doesn’t carry amounts or statuses — it tells you what to look at. Map associated_object_type to the matching GET endpoint:
associated_object_typeFetch with
paymentGET /payment/{id}
disbursementGET /disbursement/{id}
charge_authorizationGET /chargeAuthorization/{id}
bill_paymentGET /billPayment/{id}
vendorGET /vendor/{id}
checkGET /billPayment/{id} — check events carry the bill payment’s ID
Always act on the fetched object, not the event name alone. For example, payment.made for a bank (ACH) payment fires when the debit is initiated — the fetched payment will show status: "PENDING", not SUCCEEDED, until the funds actually arrive.
5

Test the loop in the sandbox

  1. Point your sandbox subscription at a webhook.site inbox (or your ngrok tunnel).
  2. Trigger events with your sandbox key: create and pay an invoice, or create a charge authorization — each fires its event within seconds.
  3. Watch the deliveries arrive, then swap in your real endpoint and re-register.

Where to go next

Webhook events

The full event catalog, delivery retries, and signature verification.

Send an invoice and get paid

The flow that fires payment.made.