Idempotency

Every creation endpoint (POST /invoices, POST /payouts, POST .../refunds) requires an Idempotency-Key header. Send the same key on a retry and you get the exact original response back. No second resource is created, no double charge happens.

The header

HeaderRequired onFormatMax length
Idempotency-KeyAll POST creation endpointsAny string; UUID v4 recommended128 characters

Missing key → 400 IDEMPOTENCY_REQUIRED. Empty string → same.

Retention window

The key + cached response are stored for 24 hours after the original successful request. After that window the key is expired. Sending the same key creates a new resource as if the key were fresh.

Replay behaviour

  • Same key + same body → HTTP status and body are replayed byte-for-byte from the cache.
  • Retries with the same key return the stored response directly, without re-processing the request.
  • One-time secrets embedded in the original response (e.g. a payment link token) are also replayed, so the caller doesn’t lose them on a retry.

Conflict: same key, different body

If you send the same Idempotency-Key with a different request body, the server returns 422 IDEMPOTENCY_KEY_REUSED_DIFFERENT_BODY. Generate a new UUID for the new request.

json
{
  "error": {
    "code": "IDEMPOTENCY_KEY_REUSED_DIFFERENT_BODY",
    "message": "same Idempotency-Key sent with a different request body",
    "request_id": "..."
  }
}

Missing key

json
{
  "error": {
    "code": "IDEMPOTENCY_REQUIRED",
    "message": "Idempotency-Key header is required for invoice creation",
    "request_id": "..."
  }
}

Code samples

Node.js

javascript
import crypto from 'node:crypto';

const idempotencyKey = crypto.randomUUID(); // UUID v4

const res = await fetch('https://swapss.lol/api/v1/merchant/invoices', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Idempotency-Key': idempotencyKey,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    amount: '5.00',
    currency: 'USDT',
    chain: 'tron',
    order_id: 'ORD-1042',
  }),
});

const invoice = await res.json();

Python

python
import uuid
import requests

idempotency_key = str(uuid.uuid4())

resp = requests.post(
    "https://swapss.lol/api/v1/merchant/invoices",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": idempotency_key,
        "Content-Type": "application/json",
    },
    json={
        "amount": "5.00",
        "currency": "USDT",
        "chain": "tron",
        "order_id": "ORD-1042",
    },
    timeout=30,
)
invoice = resp.json()

PHP

php
<?php
use Ramsey\Uuid\Uuid;

$idempotencyKey = Uuid::uuid4()->toString();

$response = $httpClient->post('https://swapss.lol/api/v1/merchant/invoices', [
    'headers' => [
        'Authorization'   => "Bearer {$apiKey}",
        'Idempotency-Key' => $idempotencyKey,
        'Content-Type'    => 'application/json',
    ],
    'json' => [
        'amount'   => '5.00',
        'currency' => 'USDT',
        'chain'    => 'tron',
        'order_id' => 'ORD-1042',
    ],
]);
$invoice = json_decode($response->getBody(), true);

Best practices

  • Generate one UUID per business attempt. If your job framework retries a task, reuse the same key across those retries. That’s the whole point.
  • Store the key alongside the order in your database before sending the request. If your process crashes, the stored key lets you safely retry.
  • Do not share keys across different operations (e.g. don’t reuse an invoice key for a payout). Each resource type has its own idempotency namespace.