Skip to main content

Webhooks API

123hub sends outgoing webhooks to merchant callback URLs when payment or payout events occur. Merchants can configure callbacks through onboarding/dashboard flows, per-payment webhook_url, or the webhook management API.

Event Types

EventDescription
payment.createdDeposit/payment was created
payment.processingPayment entered processing
payment.completedPayment completed successfully
payment.failedPayment failed
payment.cancelledPayment was cancelled
payment.refundedPayment was refunded
payout.createdWithdrawal was created
payout.completedWithdrawal completed successfully
payout.failedWithdrawal failed
Some account tooling may also expose balance.updated, merchant.updated, and webhook.test events. Payment lifecycle integrations should normally subscribe only to payment and payout events.

Delivery Payload

Outgoing MultiHub webhook deliveries use this envelope:
{
  "id": "pay_123:payment.completed",
  "created_at": "2026-04-02T08:23:04.379Z",
  "data": {
    "next": null,
    "result": {
      "payment": {
        "amount": { "value": 500000, "currency": "ARS" },
        "identifiers": {
          "c_id": "merchant-order-1",
          "h_id": "pay_123",
          "p_id": "provider-ref-123"
        },
        "status": {
          "status": "success",
          "final": true,
          "success": true,
          "error": null
        },
        "timestamps": {
          "created": "2026-04-02T08:22:21.453Z",
          "updated": "2026-04-02T08:22:22.795Z",
          "finished": "2026-04-02T08:22:21.790Z"
        },
        "destination": "in",
        "receiver": {},
        "operations": []
      }
    },
    "success": true,
    "request_id": "729aebbf-5a6b-4299-87fa-1cf05c1121a6",
    "processing_time": 0
  },
  "merchant_id": "19"
}
Internal indexing fields are stripped before delivery. Do not depend on fields prefixed with _.

Delivery Headers

HeaderDescription
Content-Typeapplication/json
X-Data-HashSHA-512 signature: SHA512(rawBody + API secret)
X-Webhook-IdDelivery/event identifier
X-Webhook-TimestampISO 8601 delivery timestamp
X-Webhook-NoncePer-delivery nonce
X-Webhook-Signature-V2Optional replay-resistant signature: SHA512(timestamp + rawBody + API secret)
Use X-Data-Hash for backward-compatible verification. Use X-Webhook-Signature-V2 when present to add timestamp binding and replay protection. Webhook deliveries created or rotated after the API secret rollout use the merchant API secret. Existing legacy webhook configurations may continue to verify with the previously issued signing secret until the merchant rotates the API secret.

Signature Verification

Verify against the exact raw request body bytes. Do not parse and re-serialize JSON before hashing.
Node.js
const crypto = require("crypto");

function verifyWebhook(rawBody, apiSecret, receivedHash) {
  const expected = crypto
    .createHash("sha512")
    .update(Buffer.concat([Buffer.from(rawBody), Buffer.from(apiSecret)]))
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(receivedHash || ""),
  );
}

function verifyWebhookV2(rawBody, apiSecret, timestamp, receivedHash) {
  const expected = crypto
    .createHash("sha512")
    .update(String(timestamp) + rawBody.toString("utf8") + apiSecret)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(receivedHash || ""),
  );
}
After verification, read payment data from payload.data.result.payment.

Retry Policy

Webhook delivery succeeds on any HTTP 2xx response. Non-2xx responses, timeouts, network errors, oversized payloads, URL validation failures, or an open circuit breaker are treated as failures. Default webhook retry settings:
SettingDefaultNotes
timeout_seconds30Configurable from 5 to 60 seconds
max_retries3Configurable from 1 to 10 attempts
retry_delay_seconds1Base delay used by exponential backoff
The retry delay uses exponential backoff with jitter:
delay = min(max(random(0, 2^(attempt - 1) * retry_delay_seconds), 1s), 24h)
Retries are processed by a scheduler. Delivery order is not guaranteed, so handlers must be idempotent.

Idempotency

Use id plus data.result.payment.status.status as a practical deduplication key. data.request_id is unique per generated callback body and should not be used as the business idempotency key.

Webhook Management

Legacy merchant webhook management endpoints under /api/v1/webhooks are no longer part of the public API. Configure merchant callback URLs through onboarding or Backoffice operations. Payment-level re-delivery remains available through the signed endpoint below.

Merchant Payment Webhook Resend

For payment-level re-delivery, merchants can also use the signed MultiHub-authenticated endpoint:
POST /api/v1/payments/{paymentId}/webhook/resend
This endpoint uses X-Data-Application-Id and X-Data-Hash, checks merchant ownership, and is throttled to 10 requests per minute. It queues a webhook for a terminal payment that has a callback URL.

Per-Payment Webhook URL

payment.in and payment.out accept params.payment.webhook_url. The URL must be an absolute public HTTP(S) URL including the protocol. The system validates it at request time and again before delivery to reduce SSRF risk.