Overview
Idempotency ensures that performing the same operation multiple times has the same effect as performing it once. Agatabo uses idempotency keys to prevent duplicate financial transactions from network issues, accidental double-clicks, or system retries.How it works: Every financial operation requires a unique
x-idempotency-key header. If the same key is used twice, the second request is rejected as a duplicate.Why Idempotency Matters
Without idempotency protection:How It Works
Database Constraint
JournalEntry model has unique constraint:- Within an organization, each
idempotencyKeycan only be used once - Attempting to create a duplicate journal entry fails with database constraint error
- Second request with same key is rejected automatically
API Requirement
All mutation operations requirex-idempotency-key header:
- Recording deposits (
POST /savings) - Recording withdrawals (
DELETE /savings/{id}) - Creating loans (
POST /loans) - Recording loan payments (
POST /loans/{id}/repay) - Recording expenses (
POST /expenses) - Creating assets (
POST /assets) - Reserve allocations (
POST /reserve-allocations) - Reserve releases (
POST /reserve-allocations/{id}/release) - Dividend distributions (
POST /dividends/pools,POST /dividends/pools/{id}/distribute) - Period closing (
POST /period-closing/close,POST /period-closing/undo)
- GET requests (read-only)
- Reports generation
- Viewing data
Request Validation
Backend validates idempotency key:Generating Idempotency Keys
Frontend responsibility: Generate unique key for each operation. Recommended format:- ✅ Use UUID v4 (cryptographically random)
- ✅ Include timestamp for easier debugging
- ✅ Store key in application state during request
- ✅ Reuse same key for retries of the SAME request
- ✅ Generate new key for new operations
- ❌ Never reuse keys across different operations
- ❌ Don’t use sequential numbers (predictable)
Idempotency Scope
Keys are scoped to organization:organizationId)
Within same organization:
Duplicate Detection
When duplicate detected: Request 1 (first time):Common Scenarios
Scenario 1: Network Timeout
Situation:- User submits deposit form
- Network timeout (30 seconds, no response)
- Frontend retries with same idempotency key
Scenario 2: Accidental Double-Click
Situation:- User double-clicks “Record Payment” button
- Two requests sent rapidly
Scenario 3: Failed Request Retry
Situation:- Transaction fails due to validation error
- User fixes error and resubmits
Scenario 4: New Transaction
Situation:- User successfully records deposit
- Wants to record another deposit
Error Handling
Missing Idempotency Key
Request without key:Duplicate Key Error
Duplicate request detected:- Check if original transaction succeeded
- If succeeded: Don’t retry, show success message
- If failed: Retry with same key
- If creating new transaction: Generate new key
Frontend Implementation Example
Complete idempotency handling:Retry Logic
Safe retry strategy:Best Practices
Idempotency implementation checklist:Key generation:
- ✅ Use UUID v4 (crypto.randomUUID())
- ✅ Generate key in frontend before request
- ✅ Store key in component state during request
- ✅ Include key in all mutation API calls
- ✅ Log keys for debugging (don’t expose to users)
- ✅ Reuse same key for network timeouts
- ✅ Reuse same key for 5xx server errors
- ✅ Generate new key for new operations
- ✅ Check transaction status before retrying duplicates
- ✅ Implement exponential backoff for retries
- ✅ Detect duplicate errors (400/409 with “idempotency” message)
- ✅ Verify original transaction status before showing error
- ✅ Show “Transaction already recorded” instead of generic error
- ✅ Prevent user confusion (don’t say “failed” if it succeeded)
- ✅ Disable submit button during request (prevent double-click)
- ✅ Show loading spinner while processing
- ✅ Clear form only after confirmed success
- ✅ Handle network timeouts gracefully (retry automatically)
- ✅ Provide clear error messages
- ✅ Test duplicate key rejection
- ✅ Test network timeout retry
- ✅ Test concurrent requests with same key
- ✅ Test key uniqueness across operations
Troubleshooting
Q: Getting “idempotency-key header is required” error A: Addx-idempotency-key header to request:
Q: Getting “duplicate transaction” error immediately A: The idempotency key was already used. Either:
- Original transaction succeeded (check transaction history)
- Reusing key from previous operation (generate new key)
Q: Need to record identical transactions A: Use different idempotency keys:
Q: Transaction failed but can’t resubmit A: If transaction failed (not created), you can retry with same key:
Q: How long are keys valid? A: Idempotency keys are permanent. Once used successfully, they cannot be reused in that organization. Implication: Don’t use predictable keys (sequential numbers, dates alone).
Q: Can I use same key in different organizations? A: Yes. Unique constraint is
(organizationId, idempotencyKey):
Related Topics
Recording Deposits
Deposit API with idempotency
Creating Loans
Loan API with idempotency
Period Closing
Period close with idempotency
Audit Trail
Track duplicate prevention