Idempotency
Network errors and timeouts can leave you uncertain whether a request succeeded. Retrying a POST request without an idempotency key can create duplicate resources — two deals for the same applicant, or a document uploaded twice. The Banklyze API supports idempotency keys so you can safely retry any creation request without risk of duplication.
How It Works
Include the Idempotency-Key header with a unique value in your POST request. The API stores the response for that key for 24 hours. If you send the same key again within that window, the API returns the original stored response immediately — without creating a new resource or running the operation again.
- Generate a unique key (UUID v4 recommended) before making the request.
- Include it as
Idempotency-Key: <your-uuid>in the request headers. - If the request times out or you receive a network error, retry the exact same request with the same key.
- The API returns the original response — the resource is not created twice.
# First request — resource created
HTTP/2 201 Created
Idempotency-Key: 7f3d9a2b-1c4e-4f8a-9b0d-2e5f6a7c8d9e
{
"id": 99,
"business_name": "Acme Trucking LLC",
"status": "new",
"created_at": "2026-03-04T10:00:00Z"
}
# Second request — same Idempotency-Key, same response returned immediately
HTTP/2 200 OK
Idempotency-Key: 7f3d9a2b-1c4e-4f8a-9b0d-2e5f6a7c8d9e
{
"id": 99,
"business_name": "Acme Trucking LLC",
"status": "new",
"created_at": "2026-03-04T10:00:00Z"
}Supported Endpoints
Idempotency keys are supported on write endpoints that create new resources. GET, PATCH, and DELETE requests are naturally idempotent and do not require or use idempotency keys.
| Name | Type | Required | Description |
|---|---|---|---|
| POST /v1/ingest | supported | Optional | Bulk CRM ingest. Use a stable batch ID or content hash as the key to safely retry failed batch imports without duplicating deals. |
| POST /v1/deals | supported | Optional | Create a new deal. Use a UUID derived from the applicant record in your system so retries never create a duplicate deal. |
| POST /v1/deals/{id}/documents | supported | Optional | Upload a document. Derive the key from the file hash or your internal document ID to prevent duplicate uploads. |
| GET /v1/* | natural | Optional | All GET endpoints are naturally idempotent — reading data never has side effects. No key needed. |
| PATCH /v1/* | natural | Optional | PATCH endpoints apply the same change regardless of how many times they are called with the same body. No key needed. |
| DELETE /v1/* | natural | Optional | DELETE endpoints are idempotent — deleting an already-deleted resource returns 404, not an error. No key needed. |
Examples
curl — create a deal
# The -H "Idempotency-Key: $(uuidgen)" flag generates a fresh UUID on macOS/Linux.
# Store the key if you may need to retry the same request.
IDEM_KEY=$(uuidgen)
curl -X POST https://api.banklyze.com/v1/deals \
-H "X-API-Key: bk_your_api_key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $IDEM_KEY" \
-d '{
"business_name": "Acme Trucking LLC",
"entity_type": "llc",
"funding_amount_requested": 75000
}'Python — create a deal with retry
import os
import uuid
import requests
API_KEY = os.environ["BANKLYZE_API_KEY"]
BASE_URL = "https://api.banklyze.com/v1"
def create_deal(business_name: str, funding_amount: float) -> dict:
"""Create a deal with an idempotency key to prevent duplicates on retry."""
idempotency_key = str(uuid.uuid4())
payload = {
"business_name": business_name,
"funding_amount_requested": funding_amount,
}
# Retry loop — safe to retry because of the idempotency key
for attempt in range(3):
try:
resp = requests.post(
f"{BASE_URL}/deals",
headers={
"X-API-Key": API_KEY,
"Idempotency-Key": idempotency_key,
},
json=payload,
timeout=30,
)
resp.raise_for_status()
return resp.json()
except requests.exceptions.Timeout:
if attempt == 2:
raise
print(f"Timeout on attempt {attempt + 1}, retrying with same key...")
raise RuntimeError("All retries failed")
deal = create_deal("Acme Trucking LLC", 75000)
print(f"Created deal {deal['id']}: {deal['business_name']}")Python — bulk ingest
import os
import uuid
import requests
API_KEY = os.environ["BANKLYZE_API_KEY"]
BASE_URL = "https://api.banklyze.com/v1"
def bulk_ingest(applications: list[dict]) -> dict:
"""Ingest multiple applications safely — idempotency key prevents duplicates."""
# Generate a stable key for this batch. If you use a content hash or
# batch ID, the key will be the same on retry, deduplicating automatically.
batch_id = str(uuid.uuid4())
resp = requests.post(
f"{BASE_URL}/ingest",
headers={
"X-API-Key": API_KEY,
"Idempotency-Key": batch_id,
},
json={"applications": applications},
timeout=60,
)
resp.raise_for_status()
return resp.json()
# Sending twice with the same key returns the original response — no duplicate
batch = [
{"business_name": "Sunrise Bakery Inc", "external_ref": "CRM-001"},
{"business_name": "Metro Auto Parts", "external_ref": "CRM-002"},
]
result = bulk_ingest(batch)
print(f"Ingested {result['created']} new, {result['skipped']} skipped")curl — upload a document
# Upload a document with an idempotency key.
# Reusing the same key for the same file prevents a duplicate upload.
IDEM_KEY=$(uuidgen) # Or derive from file hash: sha256sum statement.pdf | cut -d' ' -f1
curl -X POST "https://api.banklyze.com/v1/deals/42/documents" \
-H "X-API-Key: bk_your_api_key" \
-H "Idempotency-Key: $IDEM_KEY" \
-F "file=@/path/to/statement.pdf" \
-F "document_type=bank_statement"Key Expiration
Idempotency keys expire 24 hours after the original request. After expiration:
- Sending the same key creates a new resource as if the key had never been used.
- The cached response for the key is discarded.
This means you can safely reuse a key after 24 hours for a genuinely new request, though generating a fresh UUID is the recommended practice.
| Name | Type | Required | Description |
|---|---|---|---|
| Idempotency-Key | header | Optional | Any unique string up to 255 characters. UUID v4 format is strongly recommended (e.g. 7f3d9a2b-1c4e-4f8a-9b0d-2e5f6a7c8d9e). |
| Key TTL | 24 hours | Optional | Cached responses are retained for 24 hours from the original request time. After that, the key is treated as new. |
Error Handling
If the original request failed with a 4xx or 5xx error, the idempotency key is not consumed. You can retry with the same key and the request will be processed normally — it will not replay the error response.
| Name | Type | Required | Description |
|---|---|---|---|
| Original: 2xx | replayed | Optional | The stored response is returned immediately. The operation is not repeated. |
| Original: 4xx | not consumed | Optional | The key is discarded after a client error. Retry with the same key after fixing the issue — the request runs normally. |
| Original: 5xx | not consumed | Optional | The key is discarded after a server error. Retry with the same key — safe to do immediately or after backoff. |
| Original: network timeout | safe to retry | Optional | If you never received a response, retry with the same key. If the original succeeded server-side, the replay is returned. If it never ran, it runs now. |
Idempotency-Key with a different payload, the API returns the original cached response — not the result you intended. Always generate a new UUID for each distinct resource you want to create.# WRONG — reusing the same key for a different request body
SHARED_KEY="7f3d9a2b-1c4e-4f8a-9b0d-2e5f6a7c8d9e"
# First request: creates deal for "Acme Trucking LLC"
curl -X POST https://api.banklyze.com/v1/deals \
-H "Idempotency-Key: $SHARED_KEY" \
-d '{"business_name": "Acme Trucking LLC"}'
# Second request: WRONG — different body, same key.
# The API will return the ORIGINAL "Acme Trucking LLC" response,
# NOT create "Sunrise Bakery Inc".
curl -X POST https://api.banklyze.com/v1/deals \
-H "Idempotency-Key: $SHARED_KEY" \
-d '{"business_name": "Sunrise Bakery Inc"}'
# CORRECT — always generate a new UUID for a different resource
curl -X POST https://api.banklyze.com/v1/deals \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"business_name": "Sunrise Bakery Inc"}'