VE BankingDeveloper guide
API reference
Browse documentation

Receive signed webhooks

Verify HMAC signatures before applying payment state changes.

Register an endpoint

curlbash
curl --request POST "${API_URL}/v1/webhooks" \
  --header "content-type: application/json" \
  --header "x-api-key: ${VE_BANKING_API_KEY}" \
  --data '{
    "url": "https://merchant.example.com/webhooks/ve-banking",
    "events": ["payment.pending", "payment.completed", "payment.failed", "payment.reversed"]
  }'
Envelopejson
{
  "event": "payment.completed",
  "data": {
    "paymentId": "00000000-0000-4000-8000-000000000000",
    "status": "COMPLETED"
  },
  "timestamp": "2026-07-20T15:00:00.000Z"
}

Use POST /v1/notifications/test after registering an endpoint. Deliveries include X-Webhook-Event and X-Webhook-Signature headers.

Delivery behavior

  • Each event is attempted once. Failed deliveries are not retried.
  • Your endpoint has 10 seconds to respond.
  • Event ordering is not guaranteed; compare payment status instead of arrival order.
  • Return a 2xx response only after the event is accepted for durable processing.
  • Reconcile missed or uncertain events through the payment lookup endpoints.

Verify the HMAC signature

Compute an HMAC-SHA256 digest over the exact raw request body with your endpoint secret. Compare the received and expected signatures with a timing-safe operation before parsing or acting on the event.

Node.jsjavascript
import { createHmac, timingSafeEqual } from 'node:crypto';

export function verifyWebhook(rawBody, signature, secret) {
  if (!/^sha256=[a-f0-9]{64}$/i.test(signature)) return false;

  const expected = createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  const received = Buffer.from(signature.slice('sha256='.length), 'hex');
  const calculated = Buffer.from(expected, 'hex');

  return timingSafeEqual(received, calculated);
}