Documentation Index
Fetch the complete documentation index at: https://docs.lux-core.io/llms.txt
Use this file to discover all available pages before exploring further.
Webhooks Guide
Webhooks allow you to receive real-time HTTP notifications when events occur in your LuxCore account, such as when a payment completes or fails.How Webhooks Work
Setting Up Webhooks
Create a Webhook Endpoint
Response
Webhook Events
| Event | Description |
|---|---|
payment.created | Payment was initialized |
payment.completed | Payment completed successfully |
payment.failed | Payment failed |
payment.cancelled | Payment was cancelled |
payment.refunded | Payment was refunded |
balance.updated | Merchant balance was updated |
merchant.updated | Merchant configuration was updated |
ticket.created | A new support ticket was created |
ticket.updated | Support ticket status changed (e.g., reopened) |
ticket.comment_added | Support team replied to a ticket |
ticket.resolved | Support ticket was resolved (reserved) |
ticket.closed | Support ticket was closed |
Withdrawal-type payments (
withdrawal, withdrawal_pp) use the same payment.* events as deposits. The payout.* events from the initial release have been merged into the payment.* event namespace.Webhook Payload
All webhook payloads follow this envelope structure:Payment Fields Reference
| Field | Type | Description |
|---|---|---|
id | string | Unique payment identifier |
status | string | Current payment status (completed, failed, cancelled, expired, refunded, partial_refund) |
amount | integer | Total payment amount in minor units (centavos). Example: 100050 = 1000.50 ARS |
currency | string | ISO 4217 currency code (ARS, AUD, MXN, UYU) |
type | string | Payment type: deposit, withdrawal, deposit_pp, or withdrawal_pp |
merchant_id | integer | Your merchant ID |
merchant_reference | string | Your unique reference passed when creating the payment |
method | string | null | Payment method code (bank_transfer, spei, crypto, payid). Nullable if method not yet assigned |
fee | integer | Commission amount in minor units. Already calculated based on your merchant fee rate |
net_amount | integer | Amount you receive (deposit) or total debited (withdrawal), in minor units |
created_at | string | Payment creation timestamp (ISO 8601) |
confirmed_at | string | null | When payment was confirmed by the customer |
completed_at | string | null | When payment reached terminal completed status |
failed_at | string | null | When payment failed (if applicable) |
failure_reason | string | null | Human-readable failure description |
customer_data | object | Whitelisted metadata (see below) |
processing_time_ms | integer | Time from creation to completion in milliseconds. Only present for completed events |
refund | object | null | Refund details. Only present for payment.refunded events |
Amount, Fee, and Net Amount
All monetary values are integers in minor units (centavos). The fee is automatically calculated based on your merchant commission rate.Formula:
net_amount = amount - feeDeposit example (customer pays 1000.50 ARS, merchant fee 4.5%):amount:100050— the amount the customer paidfee:4502— commission charged (100050 × 4.5%)net_amount:95548— amount credited to your balance
amount:50000— the payout amount sent to the recipientfee:2500— commission charged (50000 × 5%)net_amount:47500— total debited from your balance: payout amount minus fee
Withdrawal Payload Example
For withdrawals, the payload includespayout details inside customer_data:
customer_data Fields
Thecustomer_data object uses a whitelist approach — only specific fields are included:
| Field | Type | Description |
|---|---|---|
note | string | Customer or merchant note |
customer_email | string | Customer email (if provided) |
customer_phone | string | Customer phone (if provided) |
customer_name | string | Customer name (if provided) |
external_id | string | External reference ID |
reference | string | Payment reference |
description | string | Payment description |
expires_at | string | Payment expiration time (ISO 8601) |
requested_payment_type | string | Original requested type (deposit or withdrawal) |
commission | object | Commission details: currency, total_minor |
payout | object | Only for withdrawals: bank_code, bank_account, recipient_name, bank_name, bsb, payid, payid_type |
Webhook Headers
Each webhook request includes these headers:| Header | Description |
|---|---|
X-Webhook-Timestamp | Unix timestamp when signature was created |
X-Webhook-Signature | HMAC-SHA256 signature for verification |
X-Webhook-Id | Unique identifier for this webhook delivery |
X-Quadpay-Secret-Version | Secret version used for signing |
Content-Type | Always application/json |
The
X-Webhook-Event and X-Webhook-Retry headers are only sent for in-request webhooks (created via the webhook_url field in payment creation). They are not included in standard admin-configured webhook deliveries.Signature Verification
Always verify webhook signatures to ensure requests are from LuxCore.Signature Format
Verification Algorithm
Implementation Examples
Replay Protection
In addition to signature verification, implement these measures to prevent replay attacks:- Check timestamp: Reject webhooks with timestamps older than 5 minutes (already shown in verification examples above)
- Store webhook IDs: Save the
X-Webhook-Idheader value and reject duplicates. This prevents replayed webhooks within the timestamp window - Distinguish retries from replays: Legitimate retries from LuxCore will have the same
X-Webhook-Id. Only process each unique webhook ID once
The combination of timestamp validation and webhook ID deduplication provides strong replay protection.
Retry Policy
If webhook delivery fails (non-2xx response or timeout), we retry with exponential backoff and full jitter:- Default retries: 3 attempts after the initial delivery (configurable per webhook)
- Backoff formula: Random delay between 0 and
2^attempt x base_delay, with a maximum cap of 24 hours - Timeout: Each delivery attempt times out after 30 seconds
| Attempt | Max Delay |
|---|---|
| Initial | Immediate |
| Retry 1 | ~2 minutes |
| Retry 2 | ~4 minutes |
| Retry 3 | ~8 minutes |
Actual delays are randomized (full jitter) to prevent thundering herd. The values above are maximums.
Testing Webhooks
Send a test event to verify your endpoint:Managing Webhooks
List Webhooks
Update Webhook
Delete Webhook
In-Request Webhooks
Instead of pre-configuring webhooks via the API, you can pass awebhook_url directly in the payment creation request. This is useful when you want per-payment notification routing or a simpler integration without managing webhook endpoints.
How It Works
- Include
webhook_urlin yourPOST /paymentsrequest - LuxCore automatically creates (or reuses) a webhook endpoint for your merchant
- Events are delivered to this URL in addition to any admin-configured webhooks
- The webhook is signed using your merchant’s default webhook secret
Example
Default Events
Ifwebhook_events is not provided, the following events are subscribed by default:
payment.createdpayment.completedpayment.failedpayment.refunded
Webhook Secret
In-request webhooks are signed using your merchant’s default webhook secret. This secret is:- Automatically generated the first time you use
webhook_url - Shared across all in-request webhooks for your merchant
- Visible in the merchant settings panel of the backoffice
- Used for HMAC-SHA256 signature verification (same algorithm as admin webhooks)
Use the same signature verification logic for in-request webhooks as for admin webhooks. The only difference is the signing secret — in-request webhooks use your merchant’s default secret instead of the per-webhook secret.
Deduplication
If you send the samewebhook_url across multiple payments, the system automatically reuses the existing webhook configuration. This means:
- No duplicate webhook endpoints are created
- Event subscriptions from the first request are preserved
- The same secret key is used for all deliveries to that URL
Differences from Admin Webhooks
| Feature | Admin Webhooks | In-Request Webhooks |
|---|---|---|
| Created via | POST /webhooks API | webhook_url field in POST /payments |
| Secret key | Per-webhook (custom or generated) | Merchant default secret (shared) |
Listed in GET /webhooks | Yes (by default) | No (use ?source=in_request to view) |
| Events | Configurable per-webhook | Configurable per-request (or defaults) |
| Deduplication | Manual (create once, reuse) | Automatic (same URL = same webhook) |
Best Practices
Always Verify Signatures
Never process webhooks without verifying the signature first
Respond Quickly
Return 200 immediately, process events asynchronously
Handle Duplicates
Use payment ID +
status_version for deduplication and orderingLog Everything
Log webhook payloads for debugging and audit trails
Ordering and Deduplication
Webhooks are delivered at-least-once and may arrive out of order. Each webhook payload includes astatus_version field that increments with every payment status change.
Recommended approach:
- Store the last processed
status_versionper payment ID - Ignore webhooks where
status_version <= stored_version(stale or duplicate) - Process only webhooks with a higher
status_version
Webhook endpoints must be publicly accessible HTTPS URLs. Self-signed certificates are not supported.
