# Inbox, Idempotency Keys and Exactly-Once Illusions — Event-Driven Architecture & CQRS

Source: https://www.skillbyai.com/en/event-driven-architecture/r-inbox

> Combine the inbox pattern and idempotency keys to make end-to-end processing safe.

## Safety on the receiving side

The **inbox pattern** is the consumer-side twin of the outbox: on receipt, store the message ID (and often the message) in an `inbox` table in the consumer's database, in the same transaction as the business change, and skip IDs already present. When a consumer calls an external API with side effects, such as a payment gateway, pass an **idempotency key** derived from the event (for example the event ID or `orderId:attempt`); well-designed APIs, including major payment providers, return the original result instead of charging twice when they see the same key. Together, **outbox + at-least-once broker + inbox/idempotent consumer + idempotency keys** give the practical effect of exactly-once processing. Remember also that some side effects cannot be made idempotent (an SMS already sent); for those, accept rare duplicates or add a check before acting.

## Calling a payment API with an idempotency key

A retry with the same key returns the original charge rather than creating a new one.

```python
import requests

def charge_for(event):
    order = event["data"]
    resp = requests.post(
        "https://payments.example.com/v1/charges",
        json={"amount": order["totalMinor"], "currency": order["currency"],
              "reference": order["orderId"]},
        headers={"Idempotency-Key": f"charge-{order['orderId']}"},
        timeout=10,
    )
    resp.raise_for_status()
    return resp.json()["chargeId"]
```

## Derive keys from the business fact

A random UUID per attempt defeats the purpose. Base the key on something stable, such as the order ID, so every retry of the same intent produces the same key.

**Quiz:** Which combination gives effectively-once processing in practice?

- [x] Outbox publishing, at-least-once delivery and idempotent consumers
- [ ] At-most-once delivery and no checks
- [ ] Synchronous calls with retries and no keys
- [ ] Two brokers in parallel

*Answer:* Outbox publishing, at-least-once delivery and idempotent consumers. Reliable publishing plus deduplicating consumers turns duplicates into no-ops.
