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

# Receiving

When an event occurs, Cobalt will send a POST request to your webhook URL with a JSON payload. The payload will include information about the event that occurred.

## Headers

Every webhook request includes the following headers:

| Header                | Description                                                     |
| --------------------- | --------------------------------------------------------------- |
| `cobalt-verification` | HMAC-SHA256 signature of the payload for verifying authenticity |
| `webhook-id`          | Unique event identifier — use this to deduplicate deliveries    |
| `webhook-timestamp`   | ISO 8601 timestamp of when the event was created                |
| `webhook-attempt`     | Delivery attempt number (starting at `1`)                       |

## Verifying Signatures

To ensure the webhook is coming from Cobalt, validate the `cobalt-verification` signature before processing the payload:

```jsx theme={null}
const crypto = require('crypto');

function verifySignature(payload, signature, secretKey) {
    const expectedSignature = crypto.createHmac('sha256', secretKey)
        .update(JSON.stringify(payload))
        .digest('hex');
    return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
}

// In your webhook handler
app.post('/webhook', (req, res) => {
    const payload = req.body;
    const signature = req.headers['cobalt-verification'];

    if (!verifySignature(payload, signature, YOUR_WEBHOOK_SECRET_KEY)) {
        return res.status(401).send('Invalid signature');
    }

    // Deduplicate using webhook-id
    const webhookId = req.headers['webhook-id'];
    if (alreadyProcessed(webhookId)) {
        return res.status(200).send('Already processed');
    }

    // Process the webhook payload
    console.log('Received valid webhook:', payload);

    res.status(200).send('Webhook received');
});
```

## Retry Behavior

If your endpoint returns a **5xx** status code or a network error occurs, Cobalt will retry delivery **once** (2 total attempts) with a 1-second delay. The retry uses a shorter 2-second timeout since your server should already be warm. Each retry increments the `webhook-attempt` header.

Cobalt does **not** retry on:

* **4xx** status codes (except 429) — these indicate a client-side configuration issue
* **Timeouts** — if your server received the request but was slow to respond, retrying could cause duplicate processing

<Note>
  Return a 2xx response as quickly as possible. Process the event asynchronously to avoid timeouts. If your endpoint takes longer than 5 seconds to respond, the request will time out.
</Note>
