Skip to main content
For an overview of our Webhooks, view our Guide. For code samples, explore our Recipes.
Webhooks are linked to your MoonPay Commerce account and are authenticated using a sharedToken — included as both a Bearer token in the Authorization header and as the key for the HMAC signature in the X-Signature header. You can generate your API key in the dashboard. Webhooks are supported for Pay Links on Solana, Ethereum, Base, Polygon, and BTC, as well as Subscriptions on Solana.

Webhook Request Headers

Every webhook delivery includes the following HTTP headers:
HeaderDescriptionIncluded In
AuthorizationBearer <sharedToken> — authenticates that the request originates from MoonPay Commerce.All webhooks
Content-TypeAlways application/json.All webhooks
X-SignatureHMAC-SHA256 hex digest of the request body, keyed with your sharedToken. Use this to verify payload integrity.Pay Link & Deposit webhooks
X-Webhook-Delivery-IdIdempotency key for the webhook delivery, in the format <event>:<key>.Deposit webhooks (DEPOSIT_TX_*, DEPOSIT_BELOW_MINIMUM, and quota events)
X-Transaction-IdIdempotency key for grouping or deduplication. For DEPOSIT_TX_*, the underlying transaction meta id; for DEPOSIT_BELOW_MINIMUM, the per-fire notification id.DEPOSIT_TX_* and DEPOSIT_BELOW_MINIMUM; not sent for quota alerts
For a step-by-step guide on verifying the X-Signature header with code examples in Node.js and Python, see Verifying Webhook Signatures in the Webhooks guide.

Pay Link Webhook Payload Example

Our webhooks include detailed information about the transaction and Pay Link — for regular Pay Links there is the CREATED event, and for subscriptions there are the STARTED, RENEWED, and ENDED events.
JSON
{  // the PaylinkEventPayload object
  "transaction": "...",  // JSON string of the transactionObject below
  "event": "CREATED", // PaylinkEvents enum - matches the webhook event type when creating the webhook
  "transactionObject": {
    "id": "65e1df4d0ce08148bc333b62",  // transaction ID
    "paylinkId": "65dc9f9f1154beaac39976c8", // Paylink ID
		"fee": "1000", // Fee (base units)
    "quantity": 1,
    "createdAt": "2024-03-01T13:59:41.303Z",
    "paymentType": "PAYLINK", // or 'PAYSTREAM'
    "meta": { // the TransactionMeta object
      "id": "65e1df4d0ce08148bc333b60",
      "amount": "9900000", // amount in minimal units (lamport/Sats/Wei)
      "senderPK": "Er3RwfYqCETBTf5RktezXaNDT3zYgwBMftxBYbX8Zk1G", // customer's wallet
      "recipientPK": "Er3RwfYqCETBTf5RktezXaNDT3zYgwBMftxBYbX8Zk1G", // merchant's recipient wallet
      "customerDetails": { 
        "country": "United States",
        "deliveryAddress": "test-address",
        "email": "test@example.com",
        "fullName": "test-full-name",
        "phoneNumber": "+0123123123123",
        "discordUsername": "a11111118100",
        "discordUser": {
          "id": "1234567890", 
          "username": "test-discord-username"
        },
        "twitterUsername": "test-x-username",
        "state": "test-state",
        "street": "test-street",
        "streetNumber": "123",
        "areaCode": "90210",
        "city": "test-city"
        "additionalJSON": "\"{\\\"test\\\": 123}\"" // any additionalJSON will be JSON stringified
      },
      "productDetails": null,
      "transactionSignature": "5AYzruixQiGX8rm279cPLo7bdqaUPYMD8Z3QnBNVz2omZHaUsUKFZRmaV8W7sAHPEyExeHkjquy8mg6LHcNktg5c",
      "transactionStatus": "SUCCESS",
      "splitRevenue": false,
      "remainingAccounts": [],
      "totalAmount": "9900000",
      "affiliateAmount": "0", // Amount sent to affiliate who referred the transaction. This will normally be "0" unless referred by affiliate
      "affiliateCode": "somecode", // Affiliate code used - this will normally be undefined
      "affiliatePublicKey": "Er3RwfYqCETBTf5RktezXaNDT3zYgwBMftxBYbX8Zk1G"; // Public key (wallet address) for affiliate who referred this transaction. This will normally be undefined
      "tokenQuote": {
        "from": "SOL",
        "fromAmountDecimal": "0.01",
        "to": "SOL",
        "toAmountMinimal": "10000000"
      },
      "submitGeolocation": "FR", // ISO 3166-1 alpha-2 country code based on IP address which submitted the transaction to our server
      "currency": {
        "id": "63430c8348c610068bcdc474",
        "blockchain": null
      }
    }
  }
}

Deposit Webhooks

Deposit webhooks cover three categories: transaction events (per deposit), below-minimum alerts (per deposit wallet), and quota alerts (per company).

Transaction Events

We emit three webhook events when deposit transactions progress:
  • DEPOSIT_TX_SUBMITTED - when the transaction is broadcast to the network, via swaps.xyz when a bridge or swap is involved.
  • DEPOSIT_TX_CONFIRMED - when the transaction is confirmed on the blockchain.
  • DEPOSIT_TX_ENRICHED - when the transaction is enriched with additional data.
Configure these on a deposit-scoped or global deposit webhook (see Create a Deposit Webhook).

Below-Minimum Deposit Alerts

The DEPOSIT_BELOW_MINIMUM is sent when funds have been received in a deposit wallet but the balance has not yet reached the minimum amount required for settlement. Use this event to notify users that additional funds are needed before their deposit can be processed.

When it fires

The event is triggered when:
  • A deposit wallet receives additional funds.
  • The wallet balance is above the dust threshold.
  • The balance remains below the minimum amount required for settlement.

Setup

Below-minimum alerts are delivered through the standard Deposit Webhook endpoint.
  1. Create a deposit webhook using POST /v1/webhook/deposit/api-key (see Create a Deposit Webhook).
  2. Both deposit-scoped webhooks (with a depositId) and global webhooks (without a depositId) receive DEPOSIT_BELOW_MINIMUM when subscribed.
  3. New deposit webhooks default to events: ["ALL"], which includes DEPOSIT_BELOW_MINIMUM. You can subscribe only to DEPOSIT_BELOW_MINIMUM if you prefer.
  4. Authenticate deliveries with Authorization and verify X-Signature like other deposit webhooks.

Below-Minimum Payload Example

JSON
{
  "event": "DEPOSIT_BELOW_MINIMUM", // Webhook event type
  "depositId": "6655001000000000000000c1", // Unique deposit identifier
  "customerId": "merchant-customer-1", // Merchant-defined customer identifier
  "currency": { // Source asset on the deposit wallet (what the user sent)
    "id": "6340313846e4f91b8abc519b",
    "name": "USD Coin",
    "decimals": 6,
    "mintAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    "coinMarketCapId": 3408,
    "symbol": "USDC",
    "blockchain": {
      "id": "6340313846e4f91b8abc515c",
      "name": "SOL",
      "symbol": "SOL"
    }
  },
  "originalAmount": "3000000", // Current wallet balance, source-currency minimal units
  "originalAmountInUSD": "3000000", // Current balance in USDC minimal units (6 decimals)
  "minimumAmount": "10000000", // Required minimum in source-currency minimal units (may be omitted)
  "minimumAmountInUSD": "10000000", // Required minimum in USDC minimal units (6 decimals)
  "amount": "0", // Not a settled deposit — always zero
  "feesPaid": "0",
  "gasCharge": "0",
  "grossAmount": "0",
  "webhookDeliveryIdempotencyKey": "DEPOSIT_BELOW_MINIMUM:6655001000000000000000f1",
  "txIdempotencyKey": "6655001000000000000000f1" // Per-fire notification id (same value in X-Transaction-Id)
}
Not a completed deposit: DEPOSIT_BELOW_MINIMUM is fired before any sweep. Use originalAmount and minimumAmount (or their USD fields) to show how much more the user must send - for example, minimumAmount - originalAmount in source units.

Deposit-Customer Quota alerts

Company-level alerts fire when your account approaches its daily deposit-customer creation limit. These are separate from DEPOSIT_TX_* events: they track how many deposit customers you create per UTC day, not individual deposit transactions.
EventNominal threshold
DEPOSIT_CUSTOMER_QUOTA_WARNING~80%
DEPOSIT_CUSTOMER_QUOTA_CRITICAL~90%
DEPOSIT_CUSTOMER_QUOTA_REACHED100%

Setup

Quota alerts are delivered through the standard Deposit Webhook endpoint.
  1. Create a global deposit webhook using POST /v1/webhook/deposit/api-key without a depositId
  2. Deposit-scoped webhooks (with a depositId) do not receive quota events.
  3. New deposit webhooks are subscribed to all deposit events (ALL) by default, which includes quota alerts. If you already have a global deposit webhook, you will receive quota events without further configuration.

Behaviour

  • Alerts are triggered as deposit customers are created throughout the day
  • Each alert type is sent once per UTC day
  • Daily limits reset at midnight UTC
  • Once the limit is reached, further create requests return 429 until reset

Quota Alert Payload Example

The event field is one of DEPOSIT_CUSTOMER_QUOTA_WARNING, DEPOSIT_CUSTOMER_QUOTA_CRITICAL, or DEPOSIT_CUSTOMER_QUOTA_REACHED.
JSON
{
  "event": "DEPOSIT_CUSTOMER_QUOTA_WARNING", //Alert type
  "companyId": "6343e77d91c393456aa56462", // Company identifer
  "currentCount": 800, // Customers created today
  "dailyLimit": 1000, // Daily creation limit
  "percentUsed": 80, // Percentage of limit used
  "resetsAt": "2026-05-24T00:00:00.000Z", //UTC reset time
  "webhookDeliveryIdempotencyKey": "DEPOSIT_CUSTOMER_QUOTA_WARNING:6343e77d91c393456aa56462:2026-05-24T00:00:00.000Z"
}

Deposit Transaction Events & Payloads

These webhook events represent different stages in a deposit transaction lifecycle. The payloads below show the data available at submission, confirmation, and enrichment.
  • DEPOSIT_TX_SUBMITTED - Initial deposit received and processing started.
  • DEPOSIT_TX_CONFIRMED - Deposit has been confirmed and settled.
  • DEPOSIT_TX_ENRICHED - Includes enriched on-chain transaction details for deposit reconciliation, confirmation tracking, and transaction history.
{
  "event": "DEPOSIT_TX_SUBMITTED", // Webhook event type
  "depositId": "dep_1234567890", // Unique deposit identifier
  "customerId": "cust_abc123", // Customer identifier
  "amount": "343000000", // Net destination amount at submission (finalised in CONFIRMED)
  "feesPaid": "7000000", // Fees charged, in smallest unit
  "gasCharge": "0", // Gas fee charged (if applicable), in smallest unit
  "grossAmount": "350000000", // Minimum expected destination amount at submission
  "currency": { // Destination currency (what the user will receive)
    "id": "currency_sol_id", // Internal currency ID
    "name": "Solana", // Currency name
    "decimals": 9, // Currency decimals
    "mintAddress": "11111111111111111111111111111111", // Token mint address (native SOL shown here)
    "coinMarketCapId": 5426, // CoinMarketCap ID
    "symbol": "SOL", // Currency symbol
    "blockchain": {
      "id": "blockchain_sol_id", // Internal blockchain ID
      "name": "SOL", // Blockchain name
      "symbol": "SOL" // Blockchain symbol
    }
  },
  "originalAmount": "343000000", // Original received

Authenticating Webhooks with Bearer Token

A unique token generated at webhook creation is included with each webhook request as an Authorization: Bearer SHARED_TOKEN header, verifying that the webhook is sent from us.
JSON
{
    "id": "636cf9c095aeedb263968ee9",
    "paylink": "636a74162e72d4ad24ac9ce9",
    "company": "6343e77d91c393456aa56462",
    "targetUrl": "https://target-url.com/post-endpoint",
    "events": [
        "CREATED"
    ]
    "sharedToken": "PGpDZdZdxjdN+VgoxR/RHItMj0gq2BK5tEZtIisfyKe4M8l0B51qED/YqSSPwfAVtonvdXKhn+9j63b4krxGWNkO3hyPQiiu1SD2uttjEAdMeUO+3QoTNAwxuH1FzwyP"
}
Note: Retain this token, as it is required to authenticate and delete webhooks if needed.

Retrieving Geolocation from the Webhook Payload

The submitGeolocation field in the webhook payload provides the ISO 3166-1 alpha-2 country code representing the location of the IP address that initiated the transaction. This field is useful for understanding the geographic source of transactions. See the code example below, taken from the webhook payload
JSON
"submitGeolocation": "FR"
// ISO 3166-1 alpha-2 country code based on IP address which submitted the 
transaction to our server
JSON
{
  "event": "DEPOSIT_TX_SUBMITTED", // Webhook event type
  "depositId": "dep_1234567890", // Unique deposit identifier
  "customerId": "cust_abc123", // Customer identifier
  "amount": "343000000", // Net destination amount at submission (finalised in CONFIRMED)
  "feesPaid": "7000000", // Fees charged, in smallest unit
  "gasCharge": "0", // Gas fee charged (if applicable), in smallest unit
  "grossAmount": "350000000", // Minimum expected destination amount at submission
  "currency": { // Destination currency (what the user will receive)
    "id": "currency_sol_id", // Internal currency ID
    "name": "Solana", // Currency name
    "decimals": 9, // Currency decimals
    "mintAddress": "11111111111111111111111111111111", // Token mint address (native SOL shown here)
    "coinMarketCapId": 5426, // CoinMarketCap ID
    "symbol": "SOL", // Currency symbol
    "blockchain": {
      "id": "blockchain_sol_id", // Internal blockchain ID
      "name": "SOL", // Blockchain name
      "symbol": "SOL" // Blockchain symbol
    }
  },
  "originalAmount": "343000000", // Original received amount at submission
  "webhookDeliveryIdempotencyKey": "DEPOSIT_TX_SUBMITTED:tx_meta_abc123",
  "txIdempotencyKey": "tx_meta_abc123",
  "submittedTransactionIdempotencyKey": "submitted_tx_abc123"
}