Docs › API reference › Idempotency

Idempotency keys: never post twice by mistake

Add an Idempotency-Key to create-a-post so a retry after a timeout never sends the same post twice.

Updated

If a request to create a post times out, you cannot tell whether OmniPosts received it. Sending it again could post twice. The Idempotency-Key header removes the doubt: send the same key and the same request again and you get the original post back instead of a second one.

Use it

Send the header on POST /v1/groups. It also works on POST /v1/ideas/{id}/promote, which creates a post the same way.

  • The key is any string of 1 to 255 characters. A UUID, or an id from your own database, works well. Anything else gets 400 invalid_request.
  • The header is optional, but send it every time. Without it, a retry is a new post.
  • A key is scoped to your organization. Two organizations can use the same key.
  • Use a new key for each different post. Reuse a key only to retry the very same request.
curl https://omniposts.dev/v1/groups \
  -H "Authorization: Bearer omniposts_..." \
  -H "Idempotency-Key: 6f1c2a52-launch-post-1" \
  -H "Content-Type: application/json" \
  -d '{"body": "We shipped.", "targets": [{"channelId": "CHANNEL_ID"}]}'

What a replay looks like

If the key and the request both match an earlier call, OmniPosts creates nothing. It answers 200 with the header Idempotent-Replayed: true, and the body is the post as it is now (so its statuses may have moved on since the first call):

The first call answers 201 (all channels accepted) or 207 (some rejected). A replay is always 200 and carries rejections too. Check the Idempotent-Replayed header to tell a replay from a first answer.

HTTP/1.1 200 OK
Idempotent-Replayed: true

Same key, different request

If you reuse a key with a request that differs, OmniPosts refuses it so it cannot guess which one you meant:

The status is 409. Pick a new key for the new request.

{
  "error": {
    "code": "idempotency_conflict",
    "message": "This Idempotency-Key was already used for a different request."
  }
}

What counts as the same request

OmniPosts compares a SHA-256 fingerprint of these parts of the request:

  • body
  • mediaIds
  • scheduledFor and timezone
  • queue, when it is true
  • the draft being published, when you call POST /v1/ideas/{id}/promote
  • targets, in the order you sent them, with each target's channelId, body, mediaIds and settings

So changing the text, adding a channel, reordering the targets, or moving the time makes it a different request. A missing top-level body or mediaIds counts the same as an empty one. On a target, leaving out mediaIds (use the post's media) is different from sending [] (no media).

The Idempotency-Key itself is not part of the fingerprint, and neither are headers such as Authorization. Sending the same JSON with a different key is a new post.

A 422 does not use up the key

The key is saved only when a post is actually created. If the request is refused, nothing is saved, so you can fix the problem and send again with the same key. This covers nothing_to_publish, plan_limit_reached, queue_empty and queue_full, and any 400.

A 207 is different. It means a post was created and some channels were rejected, so the key is used. To resend those channels, use retry, or create a new post with a new key.

Two requests at once

If two requests with the same key arrive at the same moment, one creates the post and the other gets the replay. You still end up with one post.

  • Posts for the whole create call.
  • Errors for idempotency_conflict.