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

# Idempotency and retries

> Retry requests without adding the same stamp, point award, or enrolment twice.

Networks fail in awkward places. A timeout does not tell you whether Perkstar
accepted a request, so every production integration must be safe to retry.

## Request idempotency

Send an `Idempotency-Key` with every POST request:

```http theme={null}
Idempotency-Key: order-94721-loyalty-v1
```

Use a stable value for one logical operation, not a new random value on each
retry. Perkstar keeps the result for 24 hours and returns the same outcome when
the same request is repeated.

<Warning>
  Reusing one key for a different request body is an integration error. Create a
  new key only when you intend to perform a new operation.
</Warning>

## Permanent transaction deduplication

POS and commerce integrations should also send
`transaction.external_transaction_id` to `/marketplace/accrue`.

```json theme={null}
{
  "transaction": {
    "type": "STAMP",
    "external_transaction_id": "odoo-pos-order-94721"
  }
}
```

This reference is unique for the business and remains a deduplication key after
the 24-hour request cache expires. A repeated call returns the existing
transaction with `idempotent_replay: true`.

Use an upstream ID that is:

* stable across webhook retries;
* unique to one loyalty operation;
* available again when a refund must reverse the operation; and
* namespaced when several stores can produce the same numeric order ID.

Good: `odoo:shop_12:pos_order:94721`

Risky: `94721`

Keep test and live references in separate namespaces, for example
`test:odoo:shop_12:pos_order:94721` and
`live:odoo:shop_12:pos_order:94721`. Perkstar rejects a permanent transaction
reference that already belongs to the other environment instead of silently
replaying it.

## Retry policy

| Result                            | Action                                           |
| --------------------------------- | ------------------------------------------------ |
| Network error or timeout          | Retry with the same idempotency key              |
| `429`                             | Wait for `Retry-After`, then retry               |
| `500`, `502`, `503`, `504`        | Retry with exponential backoff and jitter        |
| `400`, `403`, `404`, `409`, `422` | Fix the request or state; do not blindly retry   |
| `401`                             | Replace or rotate the credential before retrying |

A practical schedule is 1 second, 2 seconds, 4 seconds, then a background retry
queue. Cap attempts and surface terminal failures for an operator to inspect.

## Reversals

Reverse a transaction by Perkstar ID or by its original
`external_transaction_id`. Give the reversal its own idempotency key. Test and
live transactions cannot be reversed across environments.


## Related topics

- [Python SDK](/sdks/python.md)
- [Errors and request tracing](/fundamentals/errors.md)
- [Quickstart](/quickstart.md)
- [TypeScript SDK](/sdks/typescript.md)
- [Connect Odoo](/guides/odoo.md)
