# Webhooks

Monetarie delivers real-time event notifications to your application via webhooks.

## Overview

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/v1/webhooks` | POST | Register a webhook endpoint |
| `/api/v1/webhooks` | GET | List webhook endpoints |
| `/api/v1/webhooks/:id` | GET | Get webhook details |
| `/api/v1/webhooks/:id` | DELETE | Delete a webhook |
| `/api/v1/webhooks/:id/test` | POST | Send a test event (delivered only to the given webhook) |

## Register a Webhook

```http
POST /api/v1/webhooks
Authorization: Bearer {token}
Content-Type: application/json

{
  "url": "https://your-app.com/webhooks/monetarie",
  "events": ["pix.charge.paid", "pix.payout.confirmed", "pix.payout.failed"],
  "secret": "whsec_your_webhook_secret_key"
}
```

**Response:**

```json
{
  "data": {
    "id": "whk_01HQGX...",
    "url": "https://your-app.com/webhooks/monetarie",
    "events": ["pix.charge.paid", "pix.payout.confirmed", "pix.payout.failed"],
    "status": "active",
    "created_at": "2026-02-03T12:00:00Z"
  }
}
```

## Event Types

### PIX Events

| Event | Description |
|-------|-------------|
| `pix.charge.created` | A new charge was created |
| `pix.charge.paid` | A charge was paid |
| `pix.charge.expired` | A charge expired without payment |
| `pix.charge.cancelled` | A charge was cancelled |
| `pix.payout.processing` | A payout is being processed |
| `pix.payout.confirmed` | A payout was confirmed |
| `pix.payout.failed` | A payout failed |
| `pix.payout.returned` | A payout was returned |
| `pix.refund.requested` | A refund was requested |
| `pix.refund.completed` | A refund was completed |

### Account Events

| Event | Description |
|-------|-------------|
| `account.created` | A new account was created |
| `account.updated` | Account details were updated |
| `account.blocked` | An account was blocked |
| `transfer.completed` | An internal transfer completed |
| `transfer.failed` | An internal transfer failed |

## Webhook Payload

All webhook deliveries follow this format:

```json
{
  "id": "evt_01HQGX...",
  "type": "pix.charge.paid",
  "created_at": "2026-02-03T12:01:30Z",
  "data": {
    "id": "chg_01HQGX...",
    "status": "paid",
    "amount": 15000,
    "paid_amount": 15000,
    "end_to_end_id": "E1234567820260203120000000001",
    "payer": {
      "name": "Maria Silva",
      "document": "***456789**"
    },
    "paid_at": "2026-02-03T12:01:30Z"
  }
}
```

## Signature Verification

Every webhook delivery includes a signature header:

```http
X-Monetarie-Signature: sha256=a1b2c3d4e5f6...
X-Monetarie-Timestamp: 1706961600
X-Monetarie-Event-Id: evt_01HQGX...
```

### Verification Examples

**JavaScript:**

```javascript
const crypto = require('crypto');

function verifyWebhook(payload, signature, timestamp, secret) {
  const signedContent = `${timestamp}.${payload}`;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(signedContent)
    .digest('hex');
  return `sha256=${expected}` === signature;
}
```

**Elixir:**

```elixir
def verify_webhook(payload, signature, timestamp, secret) do
  signed_content = "#{timestamp}.#{payload}"
  expected = :crypto.mac(:hmac, :sha256, secret, signed_content)
             |> Base.encode16(case: :lower)
  "sha256=#{expected}" == signature
end
```

::: warning
Always verify webhook signatures before processing events. Reject any delivery with an invalid signature.
:::

## Retry Policy

Failed webhook deliveries are retried with exponential backoff:

| Attempt | Delay | Total Elapsed |
|---------|-------|---------------|
| 1 | Immediate | 0s |
| 2 | 30 seconds | 30s |
| 3 | 2 minutes | 2.5 min |
| 4 | 10 minutes | 12.5 min |
| 5 | 30 minutes | 42.5 min |
| 6 | 1 hour | 1h 42min |
| 7 | 2 hours | 3h 42min |
| 8 | 4 hours | 7h 42min |

After 8 failed attempts, the event is marked as `failed` and no further retries are made.

## Best Practices

1. **Respond quickly** — Return a 200 status within 5 seconds. Process the event asynchronously.
2. **Handle duplicates** — Use `X-Monetarie-Event-Id` to deduplicate.
3. **Verify signatures** — Always validate the `X-Monetarie-Signature` header.
4. **Use HTTPS** — Webhook URLs must use HTTPS in production.
5. **Monitor failures** — Set up alerts for webhook delivery failures.
