Webhooks

Webhooks allow your application to receive real-time notifications when payment events occur. Instead of polling for updates, monterrey.app pushes events to your server.

Webhook Events

EventDescription
payment.createdA new payment was created
payment.detectingTransaction detected, waiting for confirmations
payment.confirmedPayment confirmed on blockchain
payment.expiredPayment window expired
payment.failedPayment failed (wrong amount, wrong token, etc.)

Webhook Payload

{
  "id": "evt_1234567890",
  "event": "payment.confirmed",
  "createdAt": "2024-01-15T10:35:00Z",
  "data": {
    "id": "pay_abc123",
    "merchantRef": "order_12345",
    "amount": "1000000",
    "currency": "USDC",
    "chain": "polygon",
    "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f...",
    "status": "confirmed",
    "txHash": "0x123abc...",
    "confirmedAt": "2024-01-15T10:35:00Z",
    "metadata": {
      "customer_id": "cust_abc123"
    }
  }
}

Verifying Webhooks

All webhook payloads are signed with your webhook secret. Always verify the signature before processing events.

Signature Header

The X-Monterrey-Signature header contains the HMAC-SHA256 signature of the request body.

Verification Example

import crypto from "crypto";

function verifyWebhookSignature(
  payload: string,
  signature: string,
  secret: string
): boolean {
  const expectedSignature = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// In your webhook handler
export async function handleWebhook(req, res) {
  const signature = req.headers["x-monterrey-signature"];
  const payload = JSON.stringify(req.body);

  if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  // Process the webhook...
  res.status(200).json({ received: true });
}

Handling Webhooks

export async function handleWebhook(req, res) {
  const { event, data } = req.body;

  switch (event) {
    case "payment.confirmed":
      // Payment successful - fulfill the order
      await fulfillOrder(data.merchantRef, {
        txHash: data.txHash,
        amount: data.amount,
        currency: data.currency,
      });
      break;

    case "payment.expired":
      // Payment window closed - cancel or notify user
      await expireOrder(data.merchantRef);
      break;

    case "payment.failed":
      // Something went wrong
      await handleFailedPayment(data.merchantRef, data.failureReason);
      break;
  }

  // Always return 200 to acknowledge receipt
  res.status(200).json({ received: true });
}

Retry Policy

If your endpoint returns a non-2xx status code or times out, we'll retry the webhook:

  • Retry 1: After 1 minute
  • Retry 2: After 5 minutes
  • Retry 3: After 30 minutes
  • Retry 4: After 2 hours
  • Retry 5: After 24 hours

After 5 failed attempts, the webhook is marked as failed. You can view failed webhooks in the Developer Portal.

Best Practices

  • Return quickly: Respond with 200 within 30 seconds. Process events asynchronously if needed.
  • Handle duplicates: Use the event id to deduplicate. Webhooks may be delivered more than once.
  • Verify signatures: Always validate the webhook signature before processing.
  • Use HTTPS: Only HTTPS endpoints are supported for webhooks.
  • Log everything: Keep logs of received webhooks for debugging.

Managing Webhooks via API

Create Webhook Endpoint

curl -X POST https://api.monterrey.app/v1/webhooks \
  -H "X-API-Key: mk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yoursite.com/webhooks/monterrey",
    "events": ["payment.confirmed", "payment.expired"],
    "secret": "whsec_your_secret_here"
  }'

List Webhook Endpoints

curl https://api.monterrey.app/v1/webhooks \
  -H "X-API-Key: mk_live_your_api_key"

Delete Webhook Endpoint

curl -X DELETE https://api.monterrey.app/v1/webhooks/wh_123 \
  -H "X-API-Key: mk_live_your_api_key"
    Documentation | Monterrey