Skip to main content

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

1

Event Occurs

A payment status changes (e.g., completed, failed)
2

LuxCore Sends Notification

We send an HTTP POST request to your configured endpoint
3

You Process the Event

Your server processes the event and returns a 2xx response
4

Retry if Needed

If delivery fails, we retry with exponential backoff

Setting Up Webhooks

Create a Webhook Endpoint

Response

The webhook secret is only shown once when creating the webhook. Store it securely!

Webhook Events

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:
Field name differences: The webhook payment object uses different field names than the payment creation API response. Key differences:
  • Webhook: id → API response: transaction_id
  • Webhook: fee → API response: fee_amount
  • Webhook: net_amount → present in both (same name)
Make sure your webhook handler uses the correct field names from the webhook payload.

Payment Fields Reference

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 paid
  • fee: 4502 — commission charged (100050 × 4.5%)
  • net_amount: 95548 — amount credited to your balance
Withdrawal example (payout of 500.00 ARS, merchant fee 5%):
  • amount: 50000 — the payout amount sent to the recipient
  • fee: 2500 — commission charged (50000 × 5%)
  • net_amount: 47500 — total debited from your balance: payout amount minus fee

Withdrawal Payload Example

For withdrawals, the payload includes payout details inside customer_data:

customer_data Fields

The customer_data object uses a whitelist approach — only specific fields are included:
Internal processing data (IP addresses, internal IDs, processing metadata) is never included in webhook payloads for security reasons.

Webhook Headers

Each webhook request includes these headers:
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

Important: Always compute the HMAC on the raw request body bytes exactly as received, not on re-serialized JSON. Re-serializing (e.g., JSON.stringify(parsedObject)) can change key ordering, whitespace, or Unicode escaping, causing signature mismatches.

Implementation Examples

Replay Protection

In addition to signature verification, implement these measures to prevent replay attacks:
  1. Check timestamp: Reject webhooks with timestamps older than 5 minutes (already shown in verification examples above)
  2. Store webhook IDs: Save the X-Webhook-Id header value and reject duplicates. This prevents replayed webhooks within the timestamp window
  3. 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
Actual delays are randomized (full jitter) to prevent thundering herd. The values above are maximums.
After all retry attempts are exhausted, the webhook event is marked as failed. You can manually retry failed events via the API.

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 a webhook_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

  1. Include webhook_url in your POST /payments request
  2. LuxCore automatically creates (or reuses) a webhook endpoint for your merchant
  3. Events are delivered to this URL in addition to any admin-configured webhooks
  4. The webhook is signed using your merchant’s default webhook secret

Example

Default Events

If webhook_events is not provided, the following events are subscribed by default:
  • payment.created
  • payment.completed
  • payment.failed
  • payment.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 same webhook_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

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 ordering

Log 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 a status_version field that increments with every payment status change. Recommended approach:
  1. Store the last processed status_version per payment ID
  2. Ignore webhooks where status_version <= stored_version (stale or duplicate)
  3. Process only webhooks with a higher status_version
Webhook endpoints must be publicly accessible HTTPS URLs. Self-signed certificates are not supported.