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

# Idempotency

> Prevent duplicate operations with idempotent requests

## Overview

All `POST` requests in the HopNow API require an `Idempotency-Key` header to prevent duplicate operations. This ensures safe retries when network errors or timeouts occur.

<Note>
  Do not include an Idempotency-Key with GET, PUT, PATCH, or DELETE requests. These methods are naturally idempotent.
</Note>

## How It Works

* Include a unique `Idempotency-Key` (UUID format) in every POST request
* If a request is retried with the same key, HopNow returns the original response without performing the operation again
* This prevents duplicate payments, account creations, or other operations

## Request Example

```bash theme={null}
curl -X POST https://api.hopnow.io/v1/transfers/payouts \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_api_key" \
  -H "X-Signature: hmac_signature" \
  -H "X-Timestamp: 1234567890" \
  -H "X-Nonce: abc123..." \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -d '{
    "account_id": "acct_123",
    "beneficiary_id": "bene_456",
    "amount": "1000.00",
    "currency": "USD"
  }'
```

## Generating Keys

Use UUIDs for each new operation:

<CodeGroup>
  ```python Python theme={null}
  import uuid

  idempotency_key = str(uuid.uuid4())
  ```

  ```javascript JavaScript theme={null}
  import { v4 as uuidv4 } from 'uuid';

  const idempotencyKey = uuidv4();
  ```
</CodeGroup>

## Best Practices

### Store Keys in Your Database

Track idempotency keys with request state for reliable retry logic:

```python theme={null}
# Store key before making request
transaction = {
    "idempotency_key": str(uuid.uuid4()),
    "status": "pending",
    "request_data": payout_data
}
db.save(transaction)

# Make request with stored key
response = client.post(
    "/v1/transfers/payouts",
    json=payout_data,
    headers={"Idempotency-Key": transaction["idempotency_key"]}
)
```

### Retry with Same Key

Always reuse the same idempotency key when retrying failed requests:

```python theme={null}
def create_payout_with_retry(payout_data, max_retries=3):
    idempotency_key = str(uuid.uuid4())

    for attempt in range(max_retries):
        try:
            response = client.post(
                "/v1/transfers/payouts",
                json=payout_data,
                headers={"Idempotency-Key": idempotency_key}
            )
            return response.json()
        except TimeoutError:
            if attempt == max_retries - 1:
                raise
            time.sleep(2 ** attempt)  # Exponential backoff
```

## Common Errors

### Invalid Format

```json theme={null}
{
  "error": {
    "type": "validation_error",
    "code": "invalid_idempotency_key",
    "message": "The idempotency key must be a valid UUID."
  }
}
```

**Solution**: Use a properly formatted UUID (e.g., `550e8400-e29b-41d4-a716-446655440000`)

### Conflicting Request Body

```json theme={null}
{
  "error": {
    "type": "idempotency_error",
    "code": "idempotency_key_conflict",
    "message": "The request body does not match the original request for this idempotency key."
  }
}
```

**Solution**: Use the same request body as the original request, or generate a new idempotency key for a different operation.
