Skip to content

docs(relay): a repeated idempotency key replays, it does not 409 - #1

Merged
ZhukMax merged 1 commit into
mainfrom
docs/relay-contract-v1.7.3
Aug 25, 2026
Merged

docs(relay): a repeated idempotency key replays, it does not 409#1
ZhukMax merged 1 commit into
mainfrom
docs/relay-contract-v1.7.3

Conversation

@ZhukMax

@ZhukMax ZhukMax commented Aug 24, 2026

Copy link
Copy Markdown
Member

Follows nautilus v1.7.3, which gave /v1/relay/* a translation layer so the gateway answers the contract we publish instead of its upstream's.

Docs and one test — no behaviour change. The request this SDK builds was already the documented one.

The test asserted a promise the API does not keep

test_send_throws_on409_duplicate mocked a 409 and expected an exception. A second send with an idempotency_key that was already accepted returns 202 with the originally accepted message — same id, same timestamp. That is the more useful contract: a retry of a request whose response you never saw needs no special handling, because there is no duplicate to tell apart from a success.

Replaced with test_send_replays_a_repeated_idempotency_key, which drives two sends under one key and asserts the same message comes back. Error mapping is not lost with it — test_send_throws_verne_api_exception_on400 and test_send_throws_on401 already cover that, on statuses the API really returns.

Worth knowing: until v1.7.3 the key did nothing at all. It is a body member in our reference and a header at the upstream, and the upstream drops body members it does not recognise — so every send was non-idempotent no matter what the caller passed. The gateway now maps it across.

MessagesResource had no docblocks

So the parameters now say what they do:

  • the idempotency semantics above
  • limit above 100 is clamped to 100 rather than rejected
  • cursor takes a previous nextCursor, which is null on the last page — paginate until hasMore is false, not until data comes back empty

Until v1.7.3 cursor and eventType were forwarded to an upstream that names them differently and ignores what it does not recognise, so both were silently no-ops.

Message::$status is documented as always accepted: it records that Relay took the event, not what each subscriber endpoint did with it.

Companion PRs

Same correction in vrn-sdk-node, vrn-sdk-python and nautilus-rs.

🤖 Generated with Claude Code

The gateway grew a translation layer for `/v1/relay/*` (nautilus v1.7.3) and the
published reference was corrected to match what Relay actually does: a second
send with an `idempotency_key` that was already accepted returns 202 with the
*originally* accepted message — same id, same timestamp — rather than a 409. A
retry of a request whose response was never seen therefore needs no special
handling; there is no duplicate to tell apart from a success.

`test_send_throws_on409_duplicate` asserted the old promise. Replaced with
`test_send_replays_a_repeated_idempotency_key`, which drives two sends under one
key and asserts the same message comes back. Error mapping is not lost with it —
the 400 and 401 tests already cover that, on statuses the API really returns.

`MessagesResource` had no docblocks at all, so the parameters now say what they
do: the idempotency semantics above, `limit` clamped at 100 rather than rejected,
and `cursor`/`hasMore` — paginate until `hasMore` is false, not until `data`
comes back empty. Until v1.7.3 `cursor` and `eventType` were forwarded to an
upstream that names them differently and ignores what it does not recognise, so
both were silently no-ops.

`Message::$status` is documented as always `accepted`: it records that Relay took
the event, not what each subscriber endpoint did with it.

No behaviour change — the request this SDK builds was already the documented one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant