Repository navigation
feat: add webhook delivery retry - #18
Merged
Merged
Conversation
- New operation POST /webhooks/{webhookId}/deliveries/{deliveryId}/retry
(no body; 202 returns RetryWebhookDeliveryResponse with message and
delivery_id; 409 ApiError when the webhook is disabled, the delivery
cannot be retried or a retry is already in progress).
- WebhookDeliveryData and WebhookDeliveryListData gain next_retry_at
(optional; null when no automatic retry is scheduled).
- WebhookDeliveryData payload, response_body, response_headers and
error_message keep their types; their descriptions now say they are
always included with a team API token.
- Webhook delivery endpoints and regenerate-secret document 401, 403
and 429 bodies as either ErrorMessage or ApiError.
Co-Authored-By: Claude <noreply@anthropic.com>
- webhooks().deliveries().retry(webhook_id, delivery_id) calls
POST /webhooks/{webhookId}/deliveries/{deliveryId}/retry with the team
token and no body, and returns RetryWebhookDeliveryResponse. It sends
the delivery's original payload to the webhook again now; the retry
counts as a delivery attempt, and automatic retries continue if it
fails with a retryable error.
- A disabled webhook, a delivery that cannot be retried or a retry in
progress is Error::Conflict, with WEBHOOK_DISABLED,
WEBHOOK_DELIVERY_NOT_RETRYABLE or WEBHOOK_DELIVERY_RETRY_IN_PROGRESS
as its code.
- Test next_retry_at on deliveries (a time, null or absent).
- List retry in the README's Team API table.
Co-Authored-By: Claude <noreply@anthropic.com>
bjornftw
approved these changes
Oct 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
Adds
webhooks().deliveries().retry(webhook_id, delivery_id)for the newPOST /webhooks/{webhookId}/deliveries/{deliveryId}/retryendpoint, and regenerates the types from the latest API spec.retrysends the delivery's original payload to the webhook again now. The retry counts as a delivery attempt. If it fails with a retryable error, automatic retries continue.RetryWebhookDeliveryResponse { message, delivery_id }(HTTP 202).retrieveon thewebhooks().deliveries()sub-client, where the other delivery calls already are.Error::Conflict, anderror.code()returnsWEBHOOK_DISABLED,WEBHOOK_DELIVERY_NOT_RETRYABLEorWEBHOOK_DELIVERY_RETRY_IN_PROGRESS. 404, 429 (Retry-After), 401 and 403 map to the existing error variants. There is no new error type.Idempotency-Key, so the method has no options argument (same aswebhooks().test).retry, with a one-line note on what it does.Non-breaking. Suggested bump: minor.
API changes in the regenerated types
POST /webhooks/{webhookId}/deliveries/{deliveryId}/retryops::RetryWebhookDelivery(team token, no body, noIdempotency-Key). 202 returns the newRetryWebhookDeliveryResponse { message: String, delivery_id: String }. 409 is anApiErrorwith codeWEBHOOK_DISABLED,WEBHOOK_DELIVERY_NOT_RETRYABLEorWEBHOOK_DELIVERY_RETRY_IN_PROGRESS. 404, 401, 403 and 429 are also documented.WebhookDeliveryData,WebhookDeliveryListDatanext_retry_at: Option<Option<String>>: the date-time of the next automatic retry. It isSome(None)when no retry is scheduled andNonewhen the key is missing.WebhookDeliveryDatapayload,response_body,response_headers,error_messageregenerate-secret{ "message" }or{ "error": { "code", "message", ... } }. 429 is now described inline and no longer uses the sharedTooManyRequestsresponse. The SDK already parses both shapes. A test covers a plain-message 429 onretry.OPERATIONSUsage
Checks
All of these were run on Rust 1.98.0 (MSRV) and stable, and all passed:
scripts/generate.sh --check: generated files up to datecargo fmt --all -- --check: passedcargo clippy --all-targets --all-features --locked -- -D warnings: no warningscargo test --all-features --locked: 135 passed, 0 failed (1 ignored, as before), including the README doctestscargo build --no-default-features --locked: passedRUSTDOCFLAGS="-D warnings" cargo doc --all-features --no-deps --locked: passedOther checks:
cargo +stable package --locked: passedcargo audit: no advisories (exit 0)CI secret-pattern grep over
src tests README.md UPGRADE.md CHANGELOG.md Cargo.toml: no matchescargo semver-checks check-release --baseline-rev v2.1.0: 228 of 229 checks pass. The one failure,constructible_struct_adds_field, is for:WebhookDeliveryData.next_retry_at(this PR)WebhookDeliveryListData.next_retry_at(this PR)MessageData.resent_from_message_id(already onmain, not released yet)These are fields added to generated response types. Earlier minor releases shipped the same kind of change, so this is a minor change.
New tests in
tests/team_api.rs:retrysends a POST to/webhooks/w1/deliveries/x1/retrywith the teamAuthorizationheader. It sends no body, no content type, noX-Lettermint-Tokenand noIdempotency-Key, and it parses the 202 response..., empty) and a client that only has a sending token both returnError::Config, and no request is sent.WEBHOOK_DELIVERY_NOT_RETRYABLEbecomesError::Conflictwith that code and the message.{ "message" }body becomesError::RateLimitand readsRetry-After.next_retry_aton a retrieved delivery decodes as a time, as null and when the key is missing.The coverage test in
src/tests.rsreaches all 60 operations.Not run locally
Suggested release tag
v2.2.0. This release would also include the unreleased messages resend and per-email batch idempotency keys frommain.🤖 Generated with Claude Code