Replay a webhook delivery
/v1/webhook-endpoints/{endpoint_id}/deliveries/{delivery_id}/replayRe-enqueues one delivery, Stripe-style: creates a FRESH PENDING WebhookDelivery row for the SAME event and endpoint (attempt count 0, next attempt now) that the worker picks up on its normal cycle — the original row is never mutated, so the log keeps the full history. Allowed only when the target delivery is in a terminal state (FAILED, SUCCEEDED or DISABLED — replaying a succeeded delivery is a deliberate resend). Answers 409 `delivery_already_pending` when ANY delivery for this event+endpoint pair is already PENDING or DELIVERING (no double-enqueue) — which also makes an accidental double-click naturally safe, so this route does not use the Idempotency-Key ledger. The replayed delivery carries the SAME `webhook-id` signature header as the original (the event id), so consumer-side dedup keeps working. Replaying does NOT re-enable a disabled endpoint — the worker only delivers to enabled endpoints; re-enable via the update route first.
Request
Path parameters
| Field | Type | Description |
|---|---|---|
endpoint_idrequired | string | |
delivery_idrequired | string |
Response 202
The freshly enqueued replay delivery.
Fields
| Field | Type | Description |
|---|---|---|
idrequired | string | Stable id of the delivery row. |
event_idrequired | string | The owning WebhookEvent's id — also the `webhook-id` signature header on the wire. |
event_typerequired | string | The domain event type. |
staterequired | "PENDING" | "DELIVERING" | "SUCCEEDED" | "FAILED" | "DISABLED" | Lifecycle state of one (event, endpoint) delivery. |
attempt_countrequired | integer | Number of delivery attempts made so far. |
last_statusrequired | integer | null | HTTP status of the most recent attempt, or null before any attempt has run. |
next_attempt_atrequired | string (date-time) | null | When the next retry is scheduled, or null when not PENDING. |
created_atrequired | string (date-time) | ISO-8601 UTC. |
▸ ▾ Raw response schema
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Stable id of the delivery row."
},
"event_id": {
"type": "string",
"description": "The owning WebhookEvent's id — also the `webhook-id` signature header on the wire."
},
"event_type": {
"type": "string",
"description": "The domain event type."
},
"state": {
"$ref": "#/components/schemas/WebhookDeliveryState"
},
"attempt_count": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Number of delivery attempts made so far."
},
"last_status": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "HTTP status of the most recent attempt, or null before any attempt has run."
},
"next_attempt_at": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the next retry is scheduled, or null when not PENDING."
},
"created_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "ISO-8601 UTC."
}
},
"required": [
"id",
"event_id",
"event_type",
"state",
"attempt_count",
"last_status",
"next_attempt_at",
"created_at"
],
"additionalProperties": false,
"description": "One delivery-log row — REDACTED: never includes response bodies or the signing secret."
}Example request
curl -X POST https://app.nowgetitdone.com/v1/webhook-endpoints/whep_a1b2c3/deliveries/id_123/replay \
-H "Authorization: Bearer gid_YOUR_API_KEY"Possible errors
- 401
missing_credentialsMissing credentials - 401
invalid_api_keyInvalid API key - 401
invalid_tokenInvalid access token - 403
insufficient_scopeInsufficient scope - 403
feature_not_enabledFeature not enabled on this plan - 404
resource_not_foundResource not found - 429
rate_limitedRate limit exceeded - 429
quota_exhaustedPlan quota exhausted - 409
delivery_already_pendingA delivery for this event is already queued - 500
internal_errorInternal server error