Idempotency
Idempotency prevents the same event from being processed twice. This is critical for transactional emails -- a user should receive exactly one claim notification per claim event, even if your backend retries the API call.
How It Works
When you include an idempotency_key in your event, Synapse checks whether that key has been seen before. If it has, the event is acknowledged (202 Accepted) but not processed again.
Key Format
Idempotency keys are stored in Redis with the following format:
| Component | Description |
|---|---|
tenant_id | Your workspace's internal tenant UUID |
flow_id | The ID of the matched flow |
contact_id | The contact's internal UUID |
key | The idempotency_key you provided |
Idempotency is scoped per flow and per contact. The same idempotency_key can trigger different flows for the same contact, or the same flow for different contacts, without conflict.
TTL (Time to Live)
Idempotency keys expire after 7 days by default. After expiry, the same key can be used again and will be processed as a new event.
This covers the vast majority of use cases -- if your backend retries a failed API call, it will do so within seconds or minutes, not days.
Generating Keys
Recommended Patterns
| Pattern | Example | Use Case |
|---|---|---|
| Event type + unique ID | claim_submitted_WIC-2026-0001 | Business events with natural unique identifiers |
| Event type + user + timestamp | otp_sent_user_123_1711900800 | Events that may repeat but should be unique per time window |
| UUID | 550e8400-e29b-41d4-a716-446655440000 | When no natural key exists |
Best Practices
- Use a natural business key when available.
order_completed_ORD-2026-0042is better than a random UUID because it's meaningful and debuggable. - Include the event type in the key to prevent collisions between different event types sharing the same business ID.
- Generate the key on your backend, not in the client. This ensures consistency across retries.
Concurrent Event Handling
Synapse handles concurrent identical events safely using a two-layer approach:
Layer 1: Redis Check (Fast Path)
Before processing, the worker checks Redis for the idempotency key. This is a sub-millisecond operation that catches the vast majority of duplicates.
Layer 2: Database Constraint (Safety Net)
The flow_trips table has a unique constraint on (flow_id, contact_id, trigger_event_id). Even if two workers pass the Redis check simultaneously (a rare race condition), the database constraint prevents duplicate trips.
If the database constraint catches a duplicate, the worker logs a warning and acknowledges the message without error.
Always provide an idempotency key for transactional events (claims, orders, payments). For promotional events (newsletter triggers, campaign sends), idempotency may not be necessary since duplicate sends are less harmful.
Duplicate Detection Response
When a duplicate idempotency_key is detected at the API level:
Status: 409 Conflict
A 409 response means the original event was already accepted. Your backend should treat this as a success -- the email will be (or has been) sent based on the original event.
Events Without Idempotency Keys
If you omit the idempotency_key field, Synapse processes the event unconditionally. The database-level unique constraint on flow_trips still prevents duplicate trips for the same event record, but repeated API calls will create separate event records and potentially trigger separate flow trips.
For transactional emails (OTP, claim notifications, payment receipts), always include an idempotency key. Without one, network retries or application bugs can cause duplicate emails.