Plaidly Docs

Idempotency

Safely retry API requests with idempotency keys

Idempotency

Idempotency keys let you safely retry a request without accidentally creating duplicate resources.

How it works

Pass a unique Idempotency-Key header on the request. Keys are scoped per merchant and fingerprinted against the request payload:

  • Retrying with the same key and an identical payload within 24 hours returns the original resource with the original success status code (201/200) — this is the same body you would have gotten from the first call, byte-for-byte, not a wrapper with an idempotent flag.
  • Retrying with the same key and a different payload returns 409 (error code 5, see Error model).

Where it's live today

EndpointHeader
POST /v1/payment_sessionsIdempotency-Key
POST /v1/payoutsIdempotency-Key
POST /v1/storesIdempotency-Key
POST /v1/productsIdempotency-Key
POST /v1/plansIdempotency-Key
POST /v1/pricesIdempotency-Key
POST /v1/merchantsidempotency_key (JSON body field, not a header — registration is a special case, see Register a merchant)

The locked contract calls for Idempotency-Key header support on all mutating endpoints. As of this writing it covers payment sessions, payouts, the four catalog create endpoints above, and merchant registration (body field, not header) — the catalog's publish/archive/PATCH/DELETE endpoints do not accept Idempotency-Key yet. Check plaidly-api/api/openapi.yaml or /openapi.json for the current set before relying on it elsewhere.

Usage

curl -X POST https://api.plaidly.io/v1/payment_sessions \
  -H "X-API-Key: your_api_key" \
  -H "Idempotency-Key: ord_123-attempt-1" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 10.0,
    "expires_in": "15m",
    "paymentMethod": {
      "methodID": 0,
      "chain": "solana",
      "token": "USDC",
      "network": "mainnet"
    }
  }'

A retry with the same Idempotency-Key and the same body returns the original PaymentSession object again (status 201):

{
  "session_id": "sess_01HX9K...",
  "merchant_id": "mer_01HX...",
  "expected_amount": 10,
  "received_amount": 0,
  "address": "7EcDhSYGxXyscszYEp35KHN8vvw3svAuLKTzXwCFLtV",
  "status": "pending",
  "demo": false,
  "paymentMethod": {
    "methodID": 0,
    "chain": "solana",
    "token": "USDC",
    "network": "mainnet"
  }
}

The same applies to POST /v1/payouts — a retry with the same key and payload returns the original Payout object.

Conflicting retries

If you reuse a key with a different payload than the original request, the API rejects it:

{
  "code": 5,
  "message": "idempotency key was already used with a different payload"
}

This returns 409 Conflict. Treat it as a bug in your retry logic — generate a new key per logical attempt, not a reused one across different requests.

Key format

Use any string from 1 to 255 characters. Good patterns:

  • Order ID + attempt number: ord_123-1
  • UUID: 550e8400-e29b-41d4-a716-446655440000

Keys are scoped per merchant, so two merchants can safely reuse the same key string.

Expiry

Idempotency keys expire 24 hours after the original request. After expiry, the same key creates a new resource instead of replaying the original.

When to use

Always use an Idempotency-Key when:

  • Retrying after a network timeout (you don't know if the original request succeeded)
  • Implementing retry logic in your backend
  • Using message queues that may deliver the same message twice

On this page