Idempotency
Noticia gives you two complementary ways to make writes safe to repeat. Neither is mandatory, but using them makes your integration robust against retries and replays.
external_id: the business key
Section titled “external_id: the business key”Every entity you push carries your own external_id (your CRM customer id, your POS ticket id, your SKU). Writes are upserts keyed on it: sending the same external_id twice updates the same entity instead of creating a duplicate.
POST /v1/profilesupserts byexternal_id.POST /v1/sales-eventsrecognizes a previously ingestedexternal_idand does not double-write (it returns200instead of201).POST /v1/storesupserts byexternal_id.
This is the durable guarantee: it holds for the lifetime of the entity, not just for a short window. Always send a stable external_id.
Idempotency-Key: the transport safety net
Section titled “Idempotency-Key: the transport safety net”For protection against a lost response (you sent the request, the network dropped the reply, and you do not know whether it landed), add an Idempotency-Key header, a unique value you generate such as a UUID v4:
curl https://api.sms.noticia.ai/v1/profiles \ -X POST \ -H "x-api-key: ntca_REPLACE_ME" \ -H "Idempotency-Key: 7f1c0b9e-2a4d-4c8e-9b1a-3e5f6a7b8c9d" \ -H "Content-Type: application/json" \ -d '{ "external_id": "crm-customer-4815", "first_name": "Camille" }'await fetch('https://api.sms.noticia.ai/v1/profiles', { method: 'POST', headers: { 'x-api-key': 'ntca_REPLACE_ME', 'Idempotency-Key': '7f1c0b9e-2a4d-4c8e-9b1a-3e5f6a7b8c9d', 'Content-Type': 'application/json', }, body: JSON.stringify({ external_id: 'crm-customer-4815', first_name: 'Camille', }),});import requests
requests.post( "https://api.sms.noticia.ai/v1/profiles", headers={ "x-api-key": "ntca_REPLACE_ME", "Idempotency-Key": "7f1c0b9e-2a4d-4c8e-9b1a-3e5f6a7b8c9d", }, json={"external_id": "crm-customer-4815", "first_name": "Camille"},)- First call: runs normally, and the response is stored against the key.
- Replay (same key, same body): returns the original status and body, plus an
Idempotent-Replayed: trueheader. The action does not run again. - Conflict (same key, different body): returns
409withcode: IDEMPOTENCY_CONFLICT. A key may only be reused for an identical request.
Keys are retained for 24 hours. After that, the same key runs as a fresh request.
The header is optional on every endpoint, including POST /v1/sales-events. It is a strongly recommended safety net for retries, not a requirement.
Best practices
Section titled “Best practices”- Always send a stable
external_id. It is your permanent protection against duplicates. - Add an
Idempotency-Keywhen you retry a write, and reuse the same key across retries of one operation (one key per logical operation, not per HTTP attempt). - On a replay, the body must match byte-for-byte. Serialize deterministically (stable key order) so a retry reproduces the exact same payload.
- Persist the key alongside the operation so a process restart still retries with the original key.
- Treat
Idempotent-Replayed: trueas a log signal that a retry was absorbed.
When to use which
Section titled “When to use which”| Goal | Use |
|---|---|
| Never create a duplicate entity, ever | external_id (always) |
| Safely retry a write after a network error | Idempotency-Key (per retry) |
Use both together: external_id for long-term identity, Idempotency-Key for safe network retries.