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

# Retry & Delivery

> How AgentRef retries failed webhook deliveries and how to handle them reliably.

AgentRef automatically retries failed webhook deliveries with exponential backoff. Here's how to build a reliable webhook consumer.

## Retry Schedule

If your endpoint doesn't respond with a `2xx` status code within 30 seconds, AgentRef retries:

| Attempt | Delay      | Cumulative Time |
| ------- | ---------- | --------------- |
| 1       | Immediate  | 0               |
| 2       | 1 minute   | 1 min           |
| 3       | 5 minutes  | 6 min           |
| 4       | 15 minutes | 21 min          |
| 5       | 1 hour     | \~1.3 hours     |
| 6       | 3 hours    | \~4.3 hours     |
| 7       | 6 hours    | \~10.3 hours    |
| 8       | 12 hours   | \~22.3 hours    |
| 9       | 24 hours   | \~46.3 hours    |

After all 8 retry attempts are exhausted (9 total including the first), the message is marked as failed.

## What Counts as Failure

| Scenario                              | Retried?     |
| ------------------------------------- | ------------ |
| Non-2xx HTTP response (3xx, 4xx, 5xx) | Yes          |
| Connection timeout (>30 seconds)      | Yes          |
| DNS resolution failure                | Yes          |
| TLS/SSL error                         | Yes          |
| 2xx response                          | No (success) |

## Handling Duplicates

Your endpoint may receive the same event more than once – either from retries or from concurrent deliveries. Use the `id` field in the webhook envelope for idempotency:

```javascript theme={null}
const processedEvents = new Set() // Use a database in production

app.post('/webhooks/agentref', (req, res) => {
  const event = req.body

  // Skip if already processed
  if (processedEvents.has(event.id)) {
    return res.status(200).send('Already processed')
  }

  // Process the event
  handleEvent(event)
  processedEvents.add(event.id)

  res.status(200).send('OK')
})
```

<Tip>
  In production, store processed event IDs in your database with a unique constraint rather than an in-memory Set.
</Tip>

## Best Practices

### Respond Quickly

Return a `200` response as soon as you receive the webhook. Process the event asynchronously:

```javascript theme={null}
app.post('/webhooks/agentref', (req, res) => {
  // Respond immediately
  res.status(200).send('OK')

  // Process asynchronously
  processEventAsync(req.body).catch(console.error)
})
```

### Use a Queue

For complex processing, push webhook events to a queue (Redis, SQS, etc.) and process them with a worker:

```javascript theme={null}
app.post('/webhooks/agentref', async (req, res) => {
  await queue.push('webhook-events', req.body)
  res.status(200).send('OK')
})
```

### Monitor Endpoint Health

If your endpoint consistently fails, webhook deliveries will accumulate retries. Monitor your webhook endpoint's response times and error rates.

<Warning>
  After all retries are exhausted, failed messages are **not** automatically re-sent. Check your webhook logs in the AgentRef dashboard to identify and investigate persistent failures.
</Warning>
