> ## Documentation Index
> Fetch the complete documentation index at: https://docs.leapfinancial.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks Overview

> Receive real-time notifications about link status and user events

## What are webhooks?

Webhooks are HTTP callbacks that M2M sends to your server when events occur. Instead of polling the API for updates, you receive instant notifications when:

* A magic link is created
* A user opens a magic link
* M2M needs additional user data
* A money transfer operation is created
* A money transfer operation is cancelled

```mermaid theme={null}
sequenceDiagram
    participant Partner
    participant M2M
    participant PartnerServer as Partner Server
    participant User
    
    Partner->>M2M: Creates magic link
    M2M->>PartnerServer: POST webhook (link.created)
    PartnerServer-->>M2M: 200 OK
    
    User->>M2M: Opens magic link
    M2M->>PartnerServer: POST webhook (link.opened)
    PartnerServer-->>M2M: 200 OK
    
    Note over M2M: User data incomplete
    M2M->>PartnerServer: POST webhook (user.data_request)
    PartnerServer-->>M2M: 200 OK
    PartnerServer->>M2M: PUT /partner/data-requests/{id}
    M2M-->>PartnerServer: 200 OK (data merged)
    
    Note over User,M2M: User completes transaction
    User->>M2M: Creates operation
    M2M->>PartnerServer: POST webhook (operation.created)
    PartnerServer-->>M2M: 200 OK
    
    alt User cancels operation
    User->>M2M: Cancels operation
    M2M->>PartnerServer: POST webhook (operation.cancelled)
    PartnerServer-->>M2M: 200 OK
    end
```

## Configuration

Configure your webhook URL in the [Partner Portal](https://partner.m2m.leapfinancial.com):

1. Navigate to **Settings** > **Webhooks**
2. Enter your webhook endpoint URL (must be HTTPS)
3. Save your configuration
4. Copy your webhook secret for signature verification

<Warning>
  Your webhook endpoint must use HTTPS. HTTP endpoints are not supported for security reasons.
</Warning>

### Per-link callback URL

You can override the default webhook URL for specific links by providing a `callbackUrl` when creating the link:

```json theme={null}
{
  "referenceId": "user_123",
  "callbackUrl": "https://api.yourcompany.com/webhooks/m2m/special"
}
```

## Webhook format

All webhooks are sent as HTTP POST requests with a JSON body:

```json theme={null}
{
  "event": "link.opened",
  "timestamp": "2026-01-26T14:30:00.000Z",
  "data": {
    // Event-specific payload
  }
}
```

### Headers

Every webhook includes these headers:

| Header            | Description                            |
| ----------------- | -------------------------------------- |
| `Content-Type`    | Always `application/json`              |
| `X-M2M-Event`     | Event type (e.g., `link.opened`)       |
| `X-M2M-Timestamp` | Unix timestamp (seconds)               |
| `X-M2M-Signature` | HMAC-SHA256 signature for verification |

<Info>
  Always verify the `X-M2M-Signature` header before processing webhooks. See [Webhook Security](/m2m/webhooks/security) for implementation details.
</Info>

## Delivery guarantees

M2M implements reliable webhook delivery with automatic retries:

### Retry schedule

If your endpoint returns a non-2xx status code or times out, M2M retries with exponential backoff:

| Attempt | Delay after failure |
| ------- | ------------------- |
| 1       | Immediate           |
| 2       | 1 minute            |
| 3       | 5 minutes           |
| 4       | 30 minutes          |
| 5       | 2 hours             |

After 5 failed attempts, the webhook is marked as failed. You can replay failed webhooks from the Partner Portal.

### Timeout

Your endpoint must respond within **30 seconds**. If processing takes longer, return a `200 OK` immediately and process the webhook asynchronously.

### Success criteria

A webhook is considered delivered when your endpoint returns:

* HTTP status code `200-299`
* Within 30 seconds

## Best practices

<AccordionGroup>
  <Accordion title="Respond quickly">
    Return a `200 OK` response as soon as you receive the webhook. Process the payload asynchronously if needed.

    ```javascript theme={null}
    app.post('/webhooks/m2m', async (req, res) => {
      // Verify signature first
      if (!verifySignature(req)) {
        return res.status(401).send('Invalid signature');
      }
      
      // Acknowledge immediately
      res.status(200).send('OK');
      
      // Process asynchronously
      processWebhookAsync(req.body);
    });
    ```
  </Accordion>

  <Accordion title="Implement idempotency">
    Webhooks may be delivered more than once. Use the event data to ensure you don't process the same event twice.

    ```javascript theme={null}
    async function processWebhook(event) {
      const eventKey = `${event.event}-${event.data.linkId}-${event.timestamp}`;
      
      if (await cache.has(eventKey)) {
        console.log('Duplicate webhook, skipping');
        return;
      }
      
      await cache.set(eventKey, true, { ttl: 86400 });
      // Process the event...
    }
    ```
  </Accordion>

  <Accordion title="Verify signatures">
    Always verify the `X-M2M-Signature` header to ensure the webhook came from M2M. See [Webhook Security](/m2m/webhooks/security).
  </Accordion>

  <Accordion title="Handle unknown events gracefully">
    New event types may be added. Return `200 OK` for unknown events instead of an error.

    ```javascript theme={null}
    switch (event.event) {
      case 'link.created':
        handleLinkCreated(event.data);
        break;
      case 'link.opened':
        handleLinkOpened(event.data);
        break;
      case 'user.data_request':
        handleDataRequest(event.data);
        break;
      case 'operation.created':
        handleOperationCreated(event.data);
        break;
      case 'operation.cancelled':
        handleOperationCancelled(event.data);
        break;
      default:
        console.log(`Unknown event type: ${event.event}`);
        // Still return 200 OK
    }
    ```
  </Accordion>

  <Accordion title="Log webhook payloads">
    Store webhook payloads for debugging and audit purposes. This helps troubleshoot issues and track user journeys.
  </Accordion>

  <Accordion title="Monitor delivery health">
    Use the Partner Portal to monitor webhook delivery success rates. Set up alerts for repeated failures.
  </Accordion>
</AccordionGroup>

## Testing webhooks

### Sandbox environment

In the sandbox environment, webhooks work exactly like production. Use a tool like [ngrok](https://ngrok.com) to expose your local development server:

```bash theme={null}
ngrok http 3000
```

Then configure the ngrok URL as your webhook endpoint in the Partner Portal.

### Manual testing

1. Create a magic link in sandbox
2. Check your webhook endpoint for the `link.created` event
3. Open the link in your browser
4. Check your webhook endpoint for the `link.opened` event
5. If you didn't provide full user data, you'll receive a `user.data_request` event
6. Complete a money transfer in the widget to receive an `operation.created` event
7. Cancel an operation from the widget to receive an `operation.cancelled` event

## Troubleshooting

<AccordionGroup>
  <Accordion title="Webhooks not arriving">
    * Verify your endpoint URL is correct and uses HTTPS
    * Check that your server is publicly accessible
    * Review firewall rules that might block M2M's IP addresses
    * Check the Partner Portal for delivery errors
  </Accordion>

  <Accordion title="Signature verification failing">
    * Ensure you're using the raw request body (before JSON parsing)
    * Verify you're using the correct webhook secret
    * Check that you haven't modified the payload before verification
  </Accordion>

  <Accordion title="Webhooks arriving late">
    * Check your endpoint's response time
    * Ensure you're returning 200 OK promptly
    * Review the retry schedule - delayed webhooks may be retries
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Webhook Events" icon="list" href="/m2m/webhooks/events">
    See all event types and their payloads.
  </Card>

  <Card title="Webhook Security" icon="shield" href="/m2m/webhooks/security">
    Implement signature verification.
  </Card>
</CardGroup>
