Webhooks


Webhooks let you receive real-time notifications when events occur on your account. Instead of polling the API, configure a URL and Advance will POST a notification to it the moment something changes.

Configuring Webhooks in the Portal

You can create and manage webhooks without writing any code.

Navigate to: Settings → Webhooks

  1. Click Add Webhook
  2. Enter a Name, your HTTPS URL, and select the Event Types you want to receive
  3. Optionally add a Secret — Advance uses it to sign every delivery so your endpoint can verify the request is genuine (strongly recommended)
  4. Click Save

To pause delivery without deleting a webhook, set its status to inactive. To reactivate a suspended webhook after fixing delivery failures, set the status back to active.

Supported Events

Payment Events

EventWhen it fires
PAYMENT_CREATEDA new payment is created
PAYMENT_UPDATEDA payment's status changes
PAYMENT_REQUEST_CREATEDA new payment request is initiated
PAYMENT_REQUEST_UPDATEDA payment request's status changes (e.g. it is paid)
CHECK_DEPOSIT_CREATEDA check deposit is processed

Payment Status Lifecycle:

PendingApprovedCapturedSettled (success)

Terminal failure states: Failed, Returned, Refunded, Partially Refunded

A status of Settled is the final confirmation that a payment completed successfully.

Important: PAYMENT_CREATED and PAYMENT_UPDATED are mutually exclusive. When a payment is first recorded, only PAYMENT_CREATED fires. All subsequent status transitions fire PAYMENT_UPDATED. You will never receive both for the same status change.

Recommended: Subscribe to both PAYMENT_CREATED and PAYMENT_UPDATED for full payment lifecycle visibility.

Tracking payment requests: To be notified when a payment request you created is paid, subscribe to PAYMENT_REQUEST_UPDATED. It fires on every status change of the payment request; fetch the request via its entity_id to read the current status, paid, and paid_at. Alternatively, subscribe to PAYMENT_CREATED/PAYMENT_UPDATED — the payment record includes a payment_request_id field so you can correlate a payment back to the request it fulfilled.

User Events

EventWhen it fires
USER_INVITEDA new user is invited to the platform
USER_INVITE_RESENTA user invitation is resent
USER_DELETEDA user account is removed
USER_ROLE_UPDATEDA user's role or permissions change

API Key Events

EventWhen it fires
API_KEY_CREATEA new API key is generated
API_KEY_UPDATEAn API key's metadata is updated
API_KEY_REVOKEAn API key is revoked
API_KEY_DELETEAn API key is permanently deleted

Managing Webhooks via API

  • POST /v1/webhooks — create a webhook (provide name, url, event_types, optional secret and description)
  • PUT /v1/webhooks/{webhook_id} — update a webhook; event_types replaces the entire subscription list when included
  • GET /v1/webhooks — list all; filter by ?status=active|inactive|suspended
  • GET /v1/webhooks/{webhook_id} — fetch a single webhook
  • DELETE /v1/webhooks/{webhook_id} — permanently delete; pending retries are abandoned

Webhook Payload

Advance uses a notification pattern — the payload is intentionally minimal. It tells you what happened and to which entity; your system fetches full details from the relevant REST endpoint.

{
  "event_id": "evt_abc123xyz",
  "event_type": "PAYMENT_CREATED",
  "entity_id": "pay_456",
  "timestamp": "2025-01-15T10:30:00.123456Z",
  "webhook_id": "wh_abc123xyz"
}

After receiving a notification, fetch the entity using entity_id:

EventEndpoint
PAYMENT_CREATED / PAYMENT_UPDATEDGET /v1/payments/{entity_id}
PAYMENT_REQUEST_CREATED / PAYMENT_REQUEST_UPDATEDGET /v1/payment-requests/{entity_id}
CHECK_DEPOSIT_CREATEDGET /v1/check-deposits/{entity_id}
USER_* eventsGET /v1/users/{entity_id}
API_KEY_* eventsGET /v1/api-keys/{entity_id}

Signature Verification

When you configure a secret, every delivery includes an HMAC-SHA256 signature in the X-Webhook-Signature header. Always verify this before processing the event.

Advance computes HMAC-SHA256 over the exact bytes of the request body using your secret, and sends the result as sha256=<hex> in the header. Verify against the raw request body bytes exactly as received — do not parse and re-serialize the JSON first, as any difference in spacing or key order will change the computed signature and cause verification to fail.

Python:

import hmac
import hashlib

def verify_webhook_signature(raw_body: bytes, signature_header: str, secret: str) -> bool:
    if not signature_header or not signature_header.startswith("sha256="):
        return False
    expected = signature_header[7:]
    computed = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(computed, expected)

@app.post("/webhooks/advance")
async def handle_webhook(request: Request):
    raw_body = await request.body()
    sig = request.headers.get("X-Webhook-Signature", "")
    if not verify_webhook_signature(raw_body, sig, WEBHOOK_SECRET):
        raise HTTPException(status_code=401, detail="Invalid signature")
    payload = json.loads(raw_body)
    # handle event...

Node.js:

const crypto = require("crypto");

function verifySignature(rawBody, signatureHeader, secret) {
  if (!signatureHeader?.startsWith("sha256=")) return false;
  const expected = signatureHeader.slice(7);
  const computed = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(computed), Buffer.from(expected));
}

app.post(
  "/webhooks/advance",
  express.raw({ type: "application/json" }),
  (req, res) => {
    if (
      !verifySignature(
        req.body,
        req.headers["x-webhook-signature"],
        WEBHOOK_SECRET,
      )
    ) {
      return res.status(401).send("Invalid signature");
    }
    const payload = JSON.parse(req.body);
    // handle event...
    res.status(200).send("OK");
  },
);

Retry Policy and Suspension

If your endpoint returns a non-2xx status or doesn't respond within 10 seconds, Advance retries up to 5 times with exponential backoff: 1 min, 2 min, 4 min, 8 min, 16 min. Once the final retry fails, the delivery is marked exhausted.

A webhook is automatically suspended after 10 consecutive delivery failures. No new events are delivered until you reactivate it: PUT /v1/webhooks/{webhook_id} with { "status": "active" }. The failure counter resets to zero on any successful delivery.

Best Practices

Respond quickly. Your endpoint must reply within 10 seconds. Acknowledge the webhook immediately and process asynchronously in a background job.

Handle duplicates. Events may be delivered more than once on retry. Use event_id to deduplicate — store processed IDs and skip any already handled.

Always fetch fresh data. After receiving a notification, use entity_id to fetch current state from the REST API. Don't rely on the payload alone or assume events arrive in order — Advance does not guarantee delivery order.

Verify the signature. Always validate X-Webhook-Signature before processing. Reject requests that fail verification.