---
url: https://docs.dataflair.ai/stats/postbacks/idempotency.md
description: >-
  How DataFlair treats a repeated postback_id, and how to retry a postback
  safely without double counting a conversion.
---

# Postback idempotency

Networks fail. A request can time out after DataFlair has already recorded it. The `postback_id` field lets you retry without counting the conversion twice.

## The rule

Send a unique `postback_id` for every distinct event, and send the same `postback_id` when you retry the same event.

DataFlair looks for an earlier conversion in the same program with the same `postback_id`. It only looks back **30 days**.

| Situation | What DataFlair does |
| --- | --- |
| The `postback_id` is new for this program. | Creates the conversion. Answers `200` with `success: true` and a `conversion_id`. |
| The same `postback_id` arrived within the last 30 days. | Creates nothing. Answers `200` with `success: true` and `duplicate: true`, plus the original `conversion_id`. |
| The same `postback_id` arrived more than 30 days ago. | Treats it as a new event and creates a conversion. |
| Two requests with the same `postback_id` arrive at the same moment. | Processes one. The other may get `429` with `Concurrent delivery of the same postback_id; retry shortly.` Retry it. |

The `postback_id` is unique **per program**. The same value in two programs is two different events.

## A duplicate answer is a success

```json
{
  "success": true,
  "duplicate": true,
  "conversion_id": 42,
  "commission_id": 17,
  "message": "Postback already processed (idempotent)."
}
```

Treat `duplicate: true` the same as a normal `200`. Stop retrying.

A repeat with the same `postback_id` and different values, for example another `amount`, does not change the original conversion. DataFlair answers with the duplicate response.

## Choosing a `postback_id`

Use an id that your system already has for the event, so a retry produces the same value. A player id plus your own event id works:

```text
ftd-PLAYER-1001-evt-8892
```

Do not build the id from the current time or a random number. A retry would then carry a new id, and DataFlair would count the event again.

## When to retry

| Response | What to do |
| --- | --- |
| `200`, with or without `duplicate: true` | Stop. The event is recorded. |
| `401`, `404` or `422` | Stop. Fix the cause first. See the [error table](/stats/postbacks/contract#errors). |
| `429` | Wait, then retry with the same `postback_id`. Use the `Retry-After` header when it is present. |
| `5xx` or a timeout | Retry with the same `postback_id`. Wait longer between each attempt. |
