ZeepNotti Docs
Using the REST APIs

Idempotent Notification Requests

Safely retry a notification send without creating a duplicate.

Scope

Idempotency support exists on exactly one endpoint today: POST /v1/apps/{app_id}/notifications. No other endpoint (Templates, Segments, Webhooks, Devices, Apps) reads or honors an Idempotency-Key header - sending one anywhere else is simply ignored.

How it works

Pass an Idempotency-Key header with any value unique to that logical send (a UUID is a good default):

curl -X POST https://your-instance/v1/apps/{app_id}/notifications \
  -H "Authorization: Bearer $REST_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"target_type":"device_ids","target_value":["dev_123"],"ad_hoc_title":"Hi","ad_hoc_body":"Hello"}'
  • First call with a given key: the notification is created normally, returns 201 Created. The key, scoped to the App, is stored (Redis) along with a hash of the request body.

  • Repeat call with the same key and the same body: no new notification is created - the original notification is returned instead, with 200 OK (not 201). Safe to retry after a timeout or dropped connection.

  • Repeat call with the same key but a different body: rejected with 409 Conflict:

    { "error": { "code": "idempotency_conflict", "message": "..." } }

    This catches the case where a client accidentally reuses a key for a different send - it is treated as a bug in the caller, not silently processed.

  • No Idempotency-Key header sent at all: idempotency is skipped entirely; every call creates a new notification. The header is optional - use it whenever a retry is possible (network timeout, ambiguous response), skip it for genuinely one-off sends you never intend to retry.

Key lifetime

A key is valid for 24 hours after the first successful call. After that window, reusing the same key value starts a brand-new send (treated as if it were never used). Don't rely on keys as long-term deduplication beyond one day.

What "success" means for storage

The key is only marked as used after the notification and its per-device delivery records are created successfully. If the request fails partway through, the key is not consumed - retrying with the same key attempts a fresh creation rather than replaying a failed attempt.

Practical guidance

  • Generate one fresh key per logical send (e.g. per user action, per scheduled job run) - never reuse a key across sends that are meant to be distinct.
  • Reuse the exact same key only when retrying a request you're not sure reached the server (timeout, connection reset) - that's the case this feature exists for.
  • This mechanism is specific to Notifications; there is no equivalent guarantee yet for Template, Segment, or Webhook writes.

On this page