Docs

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":"…"}'
SituationResult
First request with the keyProcessed normally; the response is stored for 24 hours
Repeat with the same method, path and bodyThe stored response, with header idempotent-replayed: true
Repeat with a different body422 validation_error – a key belongs to exactly one request
Repeat while the first is still running409 conflict – wait and retry
First request failed with 5xxNothing 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 second POST …/conversions with the same eventId returns 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.