Idempotency
Network failures happen between your server and 3x. Two mechanisms make retries safe.
Idempotency-Key header
Endpoints marked idempotent in the reference (x-idempotent: true in OpenAPI) – creating
conversions, leads and server-side events – accept an Idempotency-Key header (any string up to 255
characters; a UUID per logical operation is ideal):
curl -X POST https://api.3x.example/v1/projects/my-site/leads \
-H "Authorization: Bearer $X3_SECRET_KEY" \
-H "Idempotency-Key: 6f1b3c7e-2a4d-4e8f-9c1a-1b2c3d4e5f60" \
-H "content-type: application/json" \
-d '{"form":"contact","email":"jane@example.com","sessionId":"…"}'| Situation | Result |
|---|---|
| First request with the key | Processed normally; the response is stored for 24 hours |
| Repeat with the same method, path and body | The stored response, with header idempotent-replayed: true |
| Repeat with a different body | 422 validation_error – a key belongs to exactly one request |
| Repeat while the first is still running | 409 conflict – wait and retry |
| First request failed with 5xx | Nothing is stored; the repeat is processed again |
Keys are scoped to the credential that sent them, so two services cannot collide.
Natural ids
Even without the header, the data model de-duplicates:
- Conversions have an
eventId(8–100 characters[A-Za-z0-9_.:-]), unique per project. A secondPOST …/conversionswith the sameeventIdreturns the existing row and uploads nothing again. Use the same id as your browser pixel (fbq('track', 'Lead', {}, {eventID})) so Meta and LinkedIn count browser and server events once. - Browser events are keyed by pageview id and counter; a batch that is sent twice (beacon plus retry) is applied once.
- **Leads from
identify()** are upserted per session and identity – calling it on every page refines one lead instead of creating many. - Web-triggered conversions exist at most once per session and definition.