Payment Webhooks
Payment webhooks let your backend receive real-time callbacks whenever a payment’s status changes. Instead of polling the Status endpoint, you can react immediately when a transaction is completed, underpaid, expired, or any other terminal state.
Why use webhooks?
Real-time
Instant status updates—no polling loops
Lower complexity
Fewer API calls, simpler state machine
Automation-ready
Trigger order fulfilment, accounting entries, customer comms, etc. as soon as events occur
Two ways to receive events
Terminal webhook (configured once)
All payments created by the terminal
Normal production flow
webhookUrls
array (sent in POST /payments/crypto
)
The single payment being created
A/B testing, dual systems, or tenant-specific routing
Fan-out delivery When you include
webhookUrls
, Bead sends every event to all of these destinations:
The terminal’s default webhook and
Each URL in the
webhookUrls
array. No change is required on the terminal if you just want extras.
1 – Configure the terminal webhook
PUT /Terminals/{terminalId}/webhook
Host: api.beadpay.io
Authorization: Bearer {access_token}
Content-Type: application/json
{
"webhookUrl": "https://yourserver.com/webhooks/payment-notifications"
}
Successful response
{
"terminalId": "{terminalId}",
"webhookUrl": "https://yourserver.com/webhooks/payment-notifications",
"updated": "2025-07-09T14:21:00Z"
}
2 – (Optional) Add per-payment endpoints
Include the webhookUrls
array when you create the hosted-payment URL:
{
"...": "... other create-payment fields ...",
"webhookUrls": [
"https://hooks.yourapp.dev/store-42",
"https://hooks.yourapp.dev/analytics"
]
}
Receiving a webhook
Bead POSTs a JSON body to each registered URL whenever statusCode
changes.
{
"trackingId": "4f181348293946cfa39b5846078c9bbc",
"paymentCode": "bAKbqtcuP5",
"statusCode": "completed",
"amounts": {
"requested": {
"inPaymentCurrency": { "amount": 1.02, "currency": { "code": "USDC", "name": "USDC Ethereum" } },
"inRequestedCurrency": { "amount": 1.00, "currency": { "code": "USD", "name": "USD" } }
},
"paid": {
"inPaymentCurrency": { "amount": 1.02, "currency": { "code": "USDC", "name": "USDC Ethereum" } }
}
},
"reference": "ORDER123",
"description": "Transaction description",
"receivedTime": "2025-07-09T14:22:18Z",
"terminalId": "{terminalId}",
"merchantId": "{merchantId}"
}
Note —
statusCode
is now the enum word (e.g."completed"
) rather than a numeric value.
Status values
created
Payment record created; awaiting funds
underpaid
Funds received < requested
overpaid
Funds received > requested
completed
Full amount confirmed (or overpaid auto-accepted)
expired
Payment window elapsed, no settlement
invalid
On-chain tx can’t be matched
cancelled
Shopper cancelled checkout
For underpaid, overpaid, expired, invalid, or cancelled outcomes, Bead prompts the shopper for an email (if not already supplied) and emails instructions for any reclaimable funds.
Securing your webhook endpoint
Signature – inspect the
x-webhook-signature
header (HMAC-SHA256).Auth header – enforce a pre-shared bearer or basic token.
IP-allowlist – optionally restrict to Bead’s published subnet list.
HTTPS – plain HTTP is rejected by the API.
Best-practice handler flow
Verify origin (signature / auth / IP).
Validate payload (
trackingId
,statusCode
, required sub-objects).Apply business logic (update DB, fulfil order, issue refund).
Acknowledge quickly with HTTP 200. Any non-2xx triggers automatic retries with exponential back-off for 24 h.
Testing in sandbox
Base URL
https://api.test.devs.beadpay.io
Point sandbox terminals at your dev webhook endpoint.
Generate test payments and force each status to ensure downstream code handles every callback.
Next steps
Combine webhooks with occasional polling (
/payments/tracking/{trackingId}
) for redundancy.Questions? Email [email protected].
Last updated