Developer 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: ${VEXPAY_API_KEY}" \
  --data '{
    "url": "https://merchant.example.com/webhooks/vexpay",
    "events": ["payment.pending", "payment.completed", "payment.failed", "merchant.verified", "merchant.rejected", "merchant.deactivated", "merchant.reactivated", "payout.completed", "payout.failed"]
  }'
Envelopejson
{
  "event": "payout.completed",
  "data": {
    "payoutId": "00000000-0000-4000-8000-000000000000",
    "externalRef": "po_a1",
    "reference": "16142941",
    "livemode": true
  },
  "timestamp": "2026-07-20T15:00:00.000Z"
}
merchant.rejectedjson
{
  "event": "merchant.rejected",
  "data": {
    "merchantId": "292d478b-a1fb-4d27-ab5c-8701ed14da88",
    "externalRef": "seller_47",
    "payoutMethodId": "0064456b-7a6a-4b7e-b0b5-d9b27b0dfa5c",
    "failureCode": "BE01",
    "reason": "Datos del cliente no corresponden a la cuenta",
    "action": "payout_method_deleted",
    "livemode": true
  },
  "timestamp": "2026-07-23T18:12:05.000Z"
}

Update subscribed events later with PATCH /v1/webhooks/:id — sending events replaces the full list. You can also change url or isActive; the signing secret cannot be rotated via PATCH.

curlbash
curl --request PATCH "${API_URL}/v1/webhooks/ENDPOINT_ID" \
  --header "content-type: application/json" \
  --header "x-api-key: ${VEXPAY_API_KEY}" \
  --data '{
    "events": ["payment.completed", "payment.failed", "payout.completed", "payout.failed"]
  }'

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);
}