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

# Delivery Tracking & Retries

> Monitor delivery attempts, understand retry behaviour, and manually retry failed deliveries.

Every time FirstPromoter attempts to send a webhook, a delivery record is created and tracked. You can inspect these records via the dashboard to debug failed deliveries.

## Delivery statuses

| Status      | Meaning                                                          |
| ----------- | ---------------------------------------------------------------- |
| `pending`   | Delivery has been queued but not yet attempted.                  |
| `delivered` | Your endpoint returned a `2xx` response.                         |
| `retrying`  | A delivery attempt failed; more retries are scheduled.           |
| `failed`    | All retry attempts were exhausted without a successful response. |

## Retry behaviour

When a delivery attempt fails (non-`2xx` response, timeout, or network error), FirstPromoter waits before retrying. The delay uses exponential backoff, roughly doubling with each attempt:

| Attempt   | Delay before retry |
| --------- | ------------------ |
| 1st retry | \~2 minutes        |
| 2nd retry | \~4 minutes        |
| 3rd retry | \~8 minutes        |
| 4th retry | \~16 minutes       |
| 5th retry | \~32 minutes       |
| 6th retry | \~1 hour           |
| 7th retry | \~2 hours          |
| 8th retry | \~4 hours          |
| 9th retry | \~8.5 hours        |

Retries stop when the number of failed attempts reaches the `max_retries` value configured on the subscription (default: `10`, configurable from `0`–`10`). After that, the delivery is marked `failed`. At the default of `10`, retries span roughly 17 hours from the first failure.

Set `max_retries` to `0` on a subscription to disable retries entirely.

## Delivery record fields

<ResponseField name="id" type="number">
  Internal ID of the delivery record.
</ResponseField>

<ResponseField name="event_id" type="string">
  The UUID of the event. Matches `event_id` in the payload.
</ResponseField>

<ResponseField name="event_type" type="string">
  The event type string, e.g. `commission.updated`.
</ResponseField>

<ResponseField name="action" type="string">
  `created`, `updated`, or `deleted`.
</ResponseField>

<ResponseField name="entity_type" type="string">
  The type of the resource, e.g. `referral`.
</ResponseField>

<ResponseField name="entity_id" type="number">
  The ID of the resource.
</ResponseField>

<ResponseField name="status" type="string">
  Current status: `pending`, `delivered`, `retrying`, or `failed`.
</ResponseField>

<ResponseField name="attempts" type="number">
  How many delivery attempts have been made so far.
</ResponseField>

<ResponseField name="response_code" type="number">
  The HTTP status code returned by your endpoint on the last attempt.
</ResponseField>

<ResponseField name="response_body" type="string">
  The first 1,000 characters of your endpoint's response body.
</ResponseField>

<ResponseField name="last_error" type="string">
  A description of the last error (e.g., timeout message, HTTP error). Up to 1,000 characters.
</ResponseField>

<ResponseField name="object_changes" type="object">
  For `updated` events, the fields that changed (same as `changes` in the payload).
</ResponseField>

<ResponseField name="payload" type="object">
  The full JSON payload that was (or will be) sent to your endpoint.
</ResponseField>

<ResponseField name="delivered_at" type="string">
  ISO 8601 timestamp of the successful delivery. `null` until delivered.
</ResponseField>

<ResponseField name="created_at" type="string">
  When the delivery record was created.
</ResponseField>

## Viewing deliveries

You can view delivery history for any subscription from the **Settings → Integrations → Webhooks** page in the dashboard. Click into a subscription to see all delivery attempts, their status, response codes, and error details.

## Manually retrying a failed delivery

You can re-attempt a delivery directly from the dashboard without waiting for the automatic retry schedule, as long as it still has retry budget remaining (its `attempts` count is below the subscription's `max_retries`). Navigate to the subscription's delivery history and click **Retry** on the delivery.

<Note>
  A delivery only reaches `failed` status once it has exhausted the subscription's `max_retries`, so a delivery that is already `failed` cannot be retried as-is — increase `max_retries` on the subscription first to free up retry budget, then retry the delivery.
</Note>

## Sending a test delivery

When setting up a new subscription you can send a test payload to your endpoint from the dashboard without needing to trigger a real event. Navigate to the subscription and click **Send test**. The test payload has `"data": { "test": true }` just like a real delivery.
