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

# Safe retries and idempotency

> Repeat a request after a timeout without creating duplicates or applying a payment twice.

PayPathIQ has no `Idempotency-Key` header. Instead, you choose the ID of each record and put it in the URL. A `PUT` to that URL is safe to repeat. This page explains what happens when you do.

## How it works

You supply an [external ID](/glossary) such as `REC_2026_0001` in the path of every `PUT`. PayPathIQ stores a fingerprint of the content it accepted under that ID. When the same ID arrives again, it compares the new content with the stored fingerprint:

* **New ID**: PayPathIQ creates the record and returns `201 Created`.
* **Same ID, identical content**: PayPathIQ changes nothing and returns `200 OK` with the current record. This is an exact replay.
* **Same ID, different content**: the result depends on the resource. A Customer or Receivable is updated (see the table below). A Payment observation is immutable, so it returns `409 payment_observation_conflict`.

Never generate a new ID for a retry. A new ID is a new record.

## Source revision

A Receivable carries a `sourceRevision`, a whole number you control. Raise it when you send a changed Receivable.

* A changed Receivable with a `sourceRevision` that is not higher than the last accepted one returns [`409 source_revision_conflict`](/problems/source_revision_conflict).
* An exact replay returns `200` before this check runs, so you can resend the same body, with the same `sourceRevision`, after a timeout.
* A change to `recoverableAmount` or `financialType` through `PUT` or `PATCH` returns `409 financial_operation_required`, even with a higher `sourceRevision`. Use a Payment observation, a Credit note, an adjustment, a write-off or a balance snapshot to change a balance.

Customers have no `sourceRevision`. The latest accepted `PUT` or `PATCH` wins. See [The latest update wins](/concepts#the-latest-update-wins).

## What a retry does

| Operation | First request | Exact retry | Changed content under the same ID |
| - | - | - | - |
| `PUT` Customer | `201` | `200`, no new revision | `200`, updated |
| `PUT` Receivable | `201` | `200`, no new revision | `200` with a higher `sourceRevision`; `409 source_revision_conflict` without one |
| `PATCH` Customer or Receivable | `200` | `200` when the merged result equals the stored record | `200`, updated. A changed Receivable needs a higher `sourceRevision`, as for `PUT`. |
| `PUT` Payment observation | `201` | `200`, nothing applied twice | `409 payment_observation_conflict` |
| `PUT` Credit note, adjustment or write-off | `201` | `200` | `409 financial_event_conflict`; these records are immutable |
| `PUT` batch | `202` | `200` with current progress, nothing queued | `409 batch_conflict` |
| `PUT` webhook endpoint | `201` with the signing secret | `200` without it | `200`, updated |
| `POST` lifecycle command | `200` | Read first; see below | Read first; see below |

A lifecycle command is not a `PUT`, so it has no replay rule. Closing a Receivable that is already closed returns `200` with its current record. Clearing a pause that is not active returns [`409 pause_not_active`](/problems/pause_not_active). Before you resend any lifecycle command, `GET` the Receivable and check its `status`.

## After a timeout

A timeout means you do not know whether PayPathIQ saved the change.

<Steps>
  <Step title="Do not change the ID or the payload">
    For a `PUT`, resend exactly what you sent, to the same URL.
  </Step>

  <Step title="Retry only when it is safe">
    Retry network failures, `429` (honor `Retry-After`) and `503` (nothing was saved). Use exponential backoff with jitter. Do not retry other `4xx` responses unchanged. See [Errors and retries](/errors-and-retries#retry-policy).
  </Step>

  <Step title="Read the result">
    `200` on the retry can mean your first request was saved, or that this retry saved it. In both cases the record is now as you sent it. `201` means the first request was never saved and this one created the record.
  </Step>

  <Step title="Reconcile if the content changed">
    If a retry returns a `409`, read the record with `GET` and compare it with your source before you send anything else. Do not resend an old Receivable body to undo a payment; the API rejects it.
  </Step>
</Steps>

## Retry a Payment observation

A Payment observation is the case that matters most. If the response to a payment is lost, resend the same body to the same ID. PayPathIQ returns `200` and does not apply the money a second time. If you send the same ID with different content, you get `409 payment_observation_conflict` and nothing changes.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.