Qubit API v1

Idempotency

Retry safely; never send a customer the same message twice.

Networks fail after the request arrived and before the answer reached you. If your POS retries a send at that moment, the customer gets the invoice twice - unless the retry can be recognised. That is what the Idempotency-Key header is for, and it is required on every write to the public API.

How it works

Idempotency-Key: INV-10025-WHATSAPP
  1. The key and a hash of the request body are looked up for your application.
  2. First time: the request runs and its answer - status and body - is stored under the key.
  3. Same key, same body, within 24 hours: the stored answer is returned verbatim, with Idempotency-Replayed: true. Nothing runs again; no second message.
  4. Same key, different body: 409 idempotency_conflict. The key was reused for something else, which is a bug on the caller's side worth surfacing.
  5. Same key while the first request is still running: 409 with a message saying to retry shortly.

Keys are scoped to your application and expire after 24 hours.

Choosing a key

Use the identity of the thing you must not do twice, not a random value:

Sending… Key
an order confirmation ORDER-10025-CONFIRMATION
an invoice on WhatsApp INV-10025-WHATSAPP
a payment reminder, one per day INV-10025-REMINDER-2026-09-15
a contact from your CRM CRM-CUSTOMER-88213

A random UUID also works and is the right choice when the request genuinely has no natural identity - but then your retry logic must reuse the same UUID, which means persisting it before the first attempt.

Retrying safely

attempt = 0
until success or attempt == 5:
    response = POST /messages with the same Idempotency-Key
    if response is 2xx: success
    if response is 429: wait Retry-After seconds
    if response is 5xx or a network error: wait 2^attempt seconds
    if response is 4xx (other than 429): stop - retrying cannot change the answer

A 409 idempotency_conflict is a stop, and a log line: two different requests shared a key.

What is not idempotent

Reads have no key and need none. Webhook deliveries to you are a different mechanism: they carry X-Event-ID, and you dedupe on it - see Webhooks.

Base URL https://communication-api.artofluminaire.com Every response carries X-Request-ID; quote it when you write to support.