When building financial applications, preventing duplicate transactions is critical. The Formance Ledger provides two mechanisms to ensure transaction uniqueness: idempotency keys and references.
Idempotency Keys#
Add an Idempotency-Key header to your request. If you execute the same request twice with the same key, the system will skip processing and return the original successful response.
How it works#
- When you send a request with an idempotency key, the ledger stores a hash of the request along with the response
- If you retry with the same key and identical request, the ledger returns the cached response with an
Idempotency-Hit: trueheader - If you retry with the same key but different request parameters, the ledger returns a validation error
Supported endpoints#
Idempotency keys work on all write endpoints:
- Create transaction (including batch and Numscript)
- Update metadata (account or transaction)
- Revert transaction
Scope#
Idempotency keys are scoped to a specific ledger. The same key can be used on different ledgers without conflict.
Example#
curl -X POST $FORMANCE_API_URL/api/ledger/v2/my-ledger/transactions \
-H "Idempotency-Key: unique-key-123" \
-H "Content-Type: application/json" \
-d '{
"postings": [
{
"source": "world",
"destination": "users:alice",
"amount": 100,
"asset": "USD/2"
}
]
}'References#
A reference is a field in the transaction body that acts as a unique identifier. If a transaction with the same reference already exists, the ledger returns an error.
Example#
fctl ledger send my-ledger --numscript '{script}'Choosing Between Idempotency Keys and References#
| Use Case | Recommended | Reason |
|---|---|---|
| Retry logic for network failures | Idempotency Key | System returns success on retry |
| Mapping to external entities (e.g., orders, refunds) | Reference | Get an error if duplicate, useful for debugging |
| Unknown or dynamic transaction identity | Idempotency Key | Works with any unique value |
Use a reference when you have a unique entity in your system that the transaction should match (e.g., a refund object creating a unique ledger transaction). You'll get an error if you try to re-submit with the same reference.
Use an idempotency key when the identity is less obvious or you want silent duplicate handling. The system will skip processing and return success on retry.
Validation Errors#
Starting with Ledger v2.2, you will receive a VALIDATION error when reusing an idempotency key with different transaction parameters than the original request.
Error format#
{
"errorCode": "VALIDATION",
"errorMessage": "invalid idempotency hash when using idempotency key 'unique-key-123', has computed 'abc123...' but 'xyz789...' is stored"
}Common causes#
- Changing any field in the request body (including metadata)
- Using a different endpoint path
- Modifying the request after a previous successful call
Best practices#
- Use unique keys for each distinct transaction - Generate a new key for each new transaction
- Keep request parameters consistent - If retrying, ensure all parameters match the original request exactly
- Use a new key if parameters change - If you need to modify the transaction, generate a new idempotency key
Changing any part of the transaction, including metadata, will trigger a validation error when reusing an idempotency key.
If a transaction was created with Ledger v2.1 or earlier, the idempotency hash stored in the database is empty. In this case, the hash validation check is skipped for backward compatibility.
Using Idempotency in Bulk Operations#
When processing bulk transactions, you can specify idempotency keys for individual elements to enable safe retries after partial failures.
Script stream format#
//script ik=transaction-001
send [USD 100] (
source = @world
destination = @alice
)
//endJSON stream format#
{
"action": "CREATE_TRANSACTION",
"ik": "transaction-001",
"data": {
"postings": [{
"source": "world",
"amount": 100,
"asset": "USD",
"destination": "alice"
}]
}
}See Bulk Processing for more details on bulk operations.