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.
Every webhook delivery includes the following HTTP headers:
Header
Description
Included In
Authorization
Bearer <sharedToken> — authenticates that the request originates from MoonPay Commerce.
All webhooks
Content-Type
Always application/json.
All webhooks
X-Signature
HMAC-SHA256 hex digest of the request body, keyed with your sharedToken. Use this to verify payload integrity.
Pay Link & Deposit webhooks
X-Webhook-Delivery-Id
Idempotency key for the webhook delivery, in the format <event>:<key>.
Deposit webhooks (DEPOSIT_TX_*, DEPOSIT_BELOW_MINIMUM, and quota events)
X-Transaction-Id
Idempotency 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.
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 } } }}
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.
{ "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.
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.
Quota alerts are delivered through the standard Deposit Webhook endpoint.
Create a global deposit webhook using POST /v1/webhook/deposit/api-key without a depositId
Deposit-scoped webhooks (with a depositId) do not receive quota events.
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.
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
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.
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"}