Terminal Webhook Events
Subscribe to real-time terminal payment events via webhooks
Terminal payments emit webhook events throughout the payment lifecycle. Subscribe to these events to receive real-time notifications when payments are requested, approved, declined, or reversed.
Event Types
Terminal Payment Events
| Event | Fired When |
|---|---|
billing.terminal_payment.requested | Payment command sent to terminal |
billing.terminal_payment.succeeded | Terminal approved the payment |
billing.terminal_payment.declined | Terminal declined the payment |
billing.terminal_payment.cancelled | Merchant cancelled before card presentation |
billing.terminal_payment.error | Terminal error or timeout |
Terminal Refund and Void Events
| Event | Fired When |
|---|---|
billing.terminal_refund.succeeded | Terminal refund approved |
billing.terminal_void.succeeded | Terminal void approved |
Event Payload
All terminal webhook events share a common payload structure:
billing.terminal_payment.succeeded
{
"id": "evt_xxxxxxxx",
"type": "billing.terminal_payment.succeeded",
"created": "2026-03-11T10:00:15Z",
"data": {
"terminal_payment_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"invoice_id": "inv_xxxxxxxx",
"device_id": "d1e2f3a4-b5c6-7890-abcd-ef1234567890",
"amount_minor": 5000,
"currency": "GBP",
"terminal_serial": "21032100001",
"auth_code": "123456",
"card_scheme": "VISA",
"masked_pan": "************1234",
"entry_mode": "contactless"
}
}billing.terminal_payment.declined
{
"id": "evt_xxxxxxxx",
"type": "billing.terminal_payment.declined",
"created": "2026-03-11T10:01:00Z",
"data": {
"terminal_payment_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"invoice_id": "inv_xxxxxxxx",
"device_id": "d1e2f3a4-b5c6-7890-abcd-ef1234567890",
"amount_minor": 5000,
"currency": "GBP",
"terminal_serial": "21032100001",
"error_message": "Insufficient funds",
"error_category": "insufficient_funds"
}
}billing.terminal_payment.error
{
"id": "evt_xxxxxxxx",
"type": "billing.terminal_payment.error",
"created": "2026-03-11T10:03:00Z",
"data": {
"terminal_payment_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"invoice_id": "inv_xxxxxxxx",
"device_id": "d1e2f3a4-b5c6-7890-abcd-ef1234567890",
"amount_minor": 5000,
"currency": "GBP",
"terminal_serial": "21032100001",
"failure_reason": "timeout",
"error_message": "Terminal did not respond within 3 minutes"
}
}billing.terminal_refund.succeeded
{
"id": "evt_xxxxxxxx",
"type": "billing.terminal_refund.succeeded",
"created": "2026-03-11T14:00:00Z",
"data": {
"terminal_payment_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"original_payment_id": "orig-payment-uuid",
"device_id": "d1e2f3a4-b5c6-7890-abcd-ef1234567890",
"amount_minor": 2500,
"currency": "GBP",
"terminal_serial": "21032100001",
"auth_code": "654321",
"card_scheme": "VISA",
"masked_pan": "************1234"
}
}Error Event Differentiation
The billing.terminal_payment.error event includes a failure_reason field to distinguish failure modes:
failure_reason | Description |
|---|---|
timeout | Terminal did not respond within 3 minutes |
terminal_error | PAX terminal returned an error (hardware issue, comms failure) |
connection_lost | Connector went offline during the transaction |
Webhook Handler Example
app.post('/webhooks/revkeen', async (req, res) => {
const event = req.body;
switch (event.type) {
case 'billing.terminal_payment.succeeded':
// Payment approved — fulfil the order
await fulfillOrder(event.data.invoice_id);
await sendReceipt(event.data.terminal_payment_id);
break;
case 'billing.terminal_payment.declined':
// Decline — notify staff with the reason
await notifyStaff(
`Payment declined: ${event.data.error_category}`
);
break;
case 'billing.terminal_payment.error':
if (event.data.failure_reason === 'timeout') {
await notifyStaff('Terminal timed out — check device connectivity');
}
break;
case 'billing.terminal_refund.succeeded':
await updateInventory(event.data.terminal_payment_id);
break;
case 'billing.terminal_void.succeeded':
await updateInventory(event.data.terminal_payment_id);
break;
}
res.json({ received: true });
});Webhook Signature Verification
Terminal webhook events use the same signature verification as all RevKeen webhooks. See the Webhooks documentation for details on verifying the X-RevKeen-Signature header.
Retry Behavior
If your webhook endpoint returns a non-2xx status code, RevKeen will retry the delivery:
- Retry schedule: 5 minutes, 30 minutes, 2 hours, 24 hours
- Maximum retries: 4 attempts after the initial delivery
- Timeout: 30 seconds per delivery attempt
Common Use Cases
| Use Case | Events to Subscribe |
|---|---|
| Update order status | billing.terminal_payment.succeeded |
| Send digital receipt | billing.terminal_payment.succeeded |
| Alert on failures | billing.terminal_payment.error, billing.terminal_payment.declined |
| Update inventory after return | billing.terminal_refund.succeeded |
| Audit trail | All billing.terminal_* events |
Related
- Webhooks -- Full webhook catalog and signature verification
- Payments API -- Terminal payment endpoints
- Best Practices -- Handling webhook delivery reliability