Idempotency
CueAPI uses at-least-once delivery. Here's how to handle duplicate deliveries.
At-least-once delivery
CueAPI guarantees at-least-once delivery. This means your handler may receive the same execution more than once in rare edge cases:
- Network timeout during delivery (CueAPI retries, but your handler already processed it)
- Worker crashes after processing but before reporting the outcome
- Infrastructure failover during delivery
This is a deliberate design choice. At-least-once is more reliable than at-most-once because it never silently drops executions.
Why not exactly-once?
Exactly-once delivery across distributed systems is extremely difficult to achieve without significant performance and complexity tradeoffs. Most production systems (Stripe webhooks, AWS SQS, Google Cloud Pub/Sub) use at-least-once delivery and recommend idempotent handlers.
Making your handler idempotent
An idempotent handler produces the same result whether it runs once or multiple times for the same execution.
Strategy 1: Track execution IDs
Store processed execution IDs and skip duplicates:
# Python / Flask example
PROCESSED = set() # Use Redis or a database in production
@app.route("/webhook", methods=["POST"])
def handle_webhook():
execution_id = request.headers.get("X-CueAPI-Execution-Id")
if execution_id in PROCESSED:
return {"status": "already processed"}, 200
# Process the execution
result = do_work(request.json)
PROCESSED.add(execution_id)
return {"status": "ok"}, 200Strategy 2: Database upserts
Use INSERT ... ON CONFLICT DO NOTHING for operations that create records:
INSERT INTO reports (execution_id, content, created_at)
VALUES ($1, $2, NOW())
ON CONFLICT (execution_id) DO NOTHING;Strategy 3: Check-before-act
Before performing a side effect, check if it's already been done:
def handle_execution(payload, execution_id):
# Check if already processed
if db.query("SELECT 1 FROM processed WHERE id = %s", execution_id):
return "already done"
# Do the work
send_email(payload["recipient"], payload["message"])
# Mark as processed
db.execute("INSERT INTO processed (id) VALUES (%s)", execution_id)Execution ID as idempotency key
The X-CueAPI-Execution-Id header (for webhooks) or the execution ID from the claim response (for workers) is your idempotency key. It's unique per execution and stable across retries.
Note
Retries of the same execution use the same execution ID. Different scheduled fires of the same cue produce different execution IDs.
Worker transport idempotency
For worker transport, the claim mechanism provides built-in protection: only one worker can claim an execution. However, if your handler crashes after completing work but before the outcome is reported, the execution may be reclaimed. Use the same idempotency strategies above.