All pages

Idempotency

Retry a call that timed out without paying for it twice.

Send an Idempotency-Key header on any POST and the call runs once, however many times it arrives.

curl https://toxicfilter.com/api/v1/text \
  -H "Authorization: Bearer tf_live_..." \
  -H "Idempotency-Key: 5f2c9a1e-comment-9931" \
  -H "Content-Type: application/json" \
  -d '{"content": "...", "reference": "comment_9931"}'

Why you want it

When a request times out, your client cannot know whether the work happened. The connection dropped; the answer it never saw may or may not exist. The correct thing for it to do is retry, and without a key that retry is a second verdict on one comment and a second charge on the account, for the model's reading as well if it read it.

One key per thing you are moderating, not one per process. A UUID generated where the comment is created, reused by every retry of that comment, is exactly right. A key that is generated once and reused for everything is a bug this API will refuse rather than paper over.

What happens

SituationAnswer
First call with this key Runs normally.
Same key, same body, original finished The original answer, replayed, with Idempotency-Replayed: true. Not charged again.
Same key, same body, original still running 409 idempotency_in_flight. Retry in a moment.
Same key, different body 422 idempotency_key_reused. Nothing is judged.
The original call failed The key is released. The next call with it is a real attempt.
Key longer than 255 characters 400 invalid_idempotency_key.

Why a different body is refused rather than answered

Replaying the first verdict for a second piece of content would tell you content B was judged when content A was. On a moderation API that is the worst failure available: it publishes something nobody looked at. Two requests are the same request only if their bodies are identical, and key order does not count as a difference, because a serialiser that reorders keys between retries is not a reason to run the work twice.

A replay is the original answer

Byte for byte, including id, took_ms and the credits block as it stood at the time. The balance has moved since; the answer to that call has not. Poll /usage for the current figure rather than reading it out of a replay.

Only successes are replayed

A 402, a 422 or a 429 releases the key, so a client that tops up its account, or fixes its request, can retry with the same key and get a real attempt. If failures were kept, the obvious reading of "idempotency" would have cost you a day of replaying your own error back at you.

How long a key lasts

24 hours, which is longer than any client retries and short enough that this never becomes a record of what you have been asking about. After that the key is forgotten and reusing it starts a fresh call.

Not the same thing as the cache

Identical content is also answered from cache, and the two are easy to confuse. The cache says this content has been judged before, gives each call its own id, and bills each one. Idempotency says this call has been made before, gives back the same id, and bills nothing. Deduplicating your traffic is the cache's job; surviving a timeout is this one's.