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

# Webhooks

> Receive real-time delivery reports and inbound messages

## Event Types

| Event | Description                          |
| ----- | ------------------------------------ |
| `dlr` | Delivery reports (status updates)    |
| `mo`  | Inbound messages (mobile-originated) |

## Creating Subscriptions

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api-message.nativehub.live/api/v1/webhooks \
    -H "Authorization: Bearer YOUR_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "url": "https://yourapp.com/webhooks",
      "events": ["dlr", "mo"],
      "secret": "your_signing_secret"
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch('https://api-message.nativehub.live/api/v1/webhooks', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_TOKEN',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      url: 'https://yourapp.com/webhooks',
      events: ['dlr', 'mo'],
      secret: 'your_signing_secret'
    })
  });
  const webhook = await response.json();
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api-message.nativehub.live/api/v1/webhooks',
      headers={
          'Authorization': 'Bearer YOUR_TOKEN',
          'Content-Type': 'application/json'
      },
      json={
          'url': 'https://yourapp.com/webhooks',
          'events': ['dlr', 'mo'],
          'secret': 'your_signing_secret'
      }
  )
  webhook = response.json()
  ```
</CodeGroup>

<Info>
  The `secret` is optional but recommended for signature verification.
</Info>

## DLR Payload

Delivery reports notify of status changes:

```json theme={null}
{
  "event": "dlr",
  "message_id": "msg_abc123",
  "status": "delivered",
  "error_code": null,
  "destination": "+1234567890",
  "delivered_at": "2026-02-14T10:30:15Z"
}
```

### Status Values

* `submitted` — Sent to carrier
* `delivered` — Confirmed delivery
* `failed` — Delivery failed
* `expired` — Validity period exceeded

### Error Codes

| Code | Description             |
| ---- | ----------------------- |
| `1`  | Invalid destination     |
| `2`  | Destination unreachable |
| `3`  | Insufficient balance    |
| `4`  | Message rejected        |

## MO Payload

Inbound messages from users:

```json theme={null}
{
  "event": "mo",
  "message_id": "msg_xyz789",
  "source": "+1234567890",
  "destination": "+9876543210",
  "content": "STOP",
  "received_at": "2026-02-14T10:25:00Z"
}
```

## HMAC Signature Verification

Verify webhook authenticity using the `X-Webhook-Signature` header:

<CodeGroup>
  ```javascript Node.js theme={null}
  const crypto = require('crypto');

  function verifySignature(payload, signature, secret) {
    const hmac = crypto.createHmac('sha256', secret);
    hmac.update(JSON.stringify(payload));
    const computed = hmac.digest('hex');

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

  app.post('/webhooks', (req, res) => {
    const signature = req.headers['x-webhook-signature'];
    const secret = 'your_signing_secret';

    if (!verifySignature(req.body, signature, secret)) {
      return res.status(401).send('Invalid signature');
    }

    // Process webhook
    res.status(200).send('OK');
  });
  ```

  ```python Python theme={null}
  import hmac
  import hashlib

  def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
      computed = hmac.new(
          secret.encode(),
          payload,
          hashlib.sha256
      ).hexdigest()

      return hmac.compare_digest(signature, computed)

  @app.route('/webhooks', methods=['POST'])
  def handle_webhook():
      signature = request.headers.get('X-Webhook-Signature')
      secret = 'your_signing_secret'

      if not verify_signature(request.data, signature, secret):
          return 'Invalid signature', 401

      # Process webhook
      return 'OK', 200
  ```
</CodeGroup>

## Retry Policy

Failed webhook deliveries are retried with exponential backoff:

| Attempt | Delay      |
| ------- | ---------- |
| 1       | Immediate  |
| 2       | 5 seconds  |
| 3       | 25 seconds |

After 3 failed attempts, the webhook is marked as failed and logged.

<Warning>
  Return a 200 status code within 5 seconds to avoid retries.
</Warning>

## Testing Webhooks

Use the test endpoint to send sample events:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api-message.nativehub.live/api/v1/webhooks/webhook_abc123/test \
    -H "Authorization: Bearer YOUR_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"event_type": "dlr"}'
  ```

  ```javascript Node.js theme={null}
  await fetch('https://api-message.nativehub.live/api/v1/webhooks/webhook_abc123/test', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_TOKEN',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({event_type: 'dlr'})
  });
  ```

  ```python Python theme={null}
  requests.post(
      'https://api-message.nativehub.live/api/v1/webhooks/webhook_abc123/test',
      headers={
          'Authorization': 'Bearer YOUR_TOKEN',
          'Content-Type': 'application/json'
      },
      json={'event_type': 'dlr'}
  )
  ```
</CodeGroup>

## Best Practices

1. **Respond quickly** — Acknowledge with 200 status before processing
2. **Process async** — Queue webhook data for background processing
3. **Verify signatures** — Always validate `X-Webhook-Signature`
4. **Handle duplicates** — Use `message_id` for idempotency
5. **Monitor failures** — Check failed webhook logs regularly

### Async Processing Example

<CodeGroup>
  ```javascript Node.js theme={null}
  const queue = require('./queue');

  app.post('/webhooks', async (req, res) => {
    // Verify signature
    if (!verifySignature(req.body, req.headers['x-webhook-signature'], secret)) {
      return res.status(401).send('Invalid signature');
    }

    // Respond immediately
    res.status(200).send('OK');

    // Queue for async processing
    await queue.add('webhook-processing', req.body);
  });
  ```

  ```python Python theme={null}
  from celery import Celery

  celery = Celery('tasks')

  @app.route('/webhooks', methods=['POST'])
  def handle_webhook():
      # Verify signature
      if not verify_signature(request.data, request.headers.get('X-Webhook-Signature'), secret):
          return 'Invalid signature', 401

      # Respond immediately
      response = ('OK', 200)

      # Queue for async processing
      process_webhook.delay(request.json)

      return response

  @celery.task
  def process_webhook(data):
      # Handle webhook data
      pass
  ```
</CodeGroup>
