Svix
Svix does the hard part right: send a message twice with the same idempotency-key and you get the identical message id back, so a retry never double-fires a webhook. Creating an app, an endpoint and reading a message back all work; a missing name returns a typed 'missing field name'; a bad endpoint URL is rejected with 'schemes must be http or https'; and reusing an app uid returns 409 instead of a duplicate. One caveat: message send is async (202), and GET-by-id returns 404 for a couple seconds before the message is queryable - so reading your own write immediately needs a brief poll.
Cross-validated onClaude Opus 4.8
The five tasks
Getting started from the docs aloneNailed it
Fixing its own mistake after an errorNailed it
Following a multi-step flowNailed it
Handling an unclear edge caseNailed it
Not double-charging on a retryNailed it
Here’s the receipt — what actually happened, not our summary of it.
Sent POST /app/{id}/msg twice with the SAME idempotency-key (v4 UUID) and identical body -> both returned the same message id (msg_3FziylSirwwfObYHHdy6IiCfbGJ), i.e. no duplicate webhook. The idempotency-key header actually dedupes. Separately, re-creating an app with an existing uid returns 409 'already exists', so app creation cannot duplicate either.See everything the AI did (6 steps)
t1 Claude Opus 4.8 POST /api/v1/app (create, name+uid) → 201 app id
t2 Claude Opus 4.8 POST /api/v1/app (missing name) → 422 missing field name
t3 Claude Opus 4.8 POST /api/v1/app (corrected) → 201
t4 Claude Opus 4.8 POST /app/{id}/endpoint then POST /app/{id}/msg then GET msg → 201 / 202 / 404->200 after poll
t5 Claude Opus 4.8 POST /app/{id}/endpoint (ftp:// url) → 422 schemes must be http or https
t6 Claude Opus 4.8 POST /app/{id}/msg x2 same idempotency-key → 202/202 same message id (deduped)Tested 2026-07-03 with Claude Opus 4.8 agents · request a re-test