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
- Click Add Webhook
- Enter a Name, your HTTPS URL, and select the Event Types you want to receive
- Optionally add a Secret — Advance uses it to sign every delivery so your endpoint can verify the request is genuine (strongly recommended)
- 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
| Event | When it fires |
|---|---|
PAYMENT_CREATED | A new payment is created |
PAYMENT_UPDATED | A payment's status changes |
PAYMENT_REQUEST_CREATED | A new payment request is initiated |
PAYMENT_REQUEST_UPDATED | A payment request's status changes (e.g. it is paid) |
CHECK_DEPOSIT_CREATED | A check deposit is processed |
Payment Status Lifecycle:
Pending → Approved → Captured → Settled (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
| Event | When it fires |
|---|---|
USER_INVITED | A new user is invited to the platform |
USER_INVITE_RESENT | A user invitation is resent |
USER_DELETED | A user account is removed |
USER_ROLE_UPDATED | A user's role or permissions change |
API Key Events
| Event | When it fires |
|---|---|
API_KEY_CREATE | A new API key is generated |
API_KEY_UPDATE | An API key's metadata is updated |
API_KEY_REVOKE | An API key is revoked |
API_KEY_DELETE | An API key is permanently deleted |
Managing Webhooks via API
POST /v1/webhooks— create a webhook (providename,url,event_types, optionalsecretanddescription)PUT /v1/webhooks/{webhook_id}— update a webhook;event_typesreplaces the entire subscription list when includedGET /v1/webhooks— list all; filter by?status=active|inactive|suspendedGET /v1/webhooks/{webhook_id}— fetch a single webhookDELETE /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:
| Event | Endpoint |
|---|---|
PAYMENT_CREATED / PAYMENT_UPDATED | GET /v1/payments/{entity_id} |
PAYMENT_REQUEST_CREATED / PAYMENT_REQUEST_UPDATED | GET /v1/payment-requests/{entity_id} |
CHECK_DEPOSIT_CREATED | GET /v1/check-deposits/{entity_id} |
USER_* events | GET /v1/users/{entity_id} |
API_KEY_* events | GET /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.
