Resend webhook deliveries

Manually re-deliver failed webhook events — optionally to a different endpoint.

One call, two ways to say WHICH deliveries. The simple way: pass a query — endpoint, event type, time window — and the server finds every failed delivery that matches and re-posts it (newest first, up to limit). The precise way: pass exact deliveries pairs when you already know them. Each accepted delivery is re-sent exactly once in the background — manual resends never enter the automatic retry schedule — and lands as a new manual attempt in the delivery history. Pass target_webhook_id with either mode to redirect every resend to another endpoint of your account (for example, after replacing a broken receiver).

POST /v3/webhooks/deliveries/resend

No listing, no pair-building: describe the failures and the server does the rest. All filters are optional — an empty query: {} means "every failed delivery of the last 24 hours, on any endpoint".

query object
ParameterTypeRequiredDescription
webhook_idUUIDOptionalOnly deliveries to this endpoint. Omit to cover every endpoint of your account.
eventstringOptionalOnly deliveries of this event type — e.g. bounced, delivered, complained.
sinceISO-8601OptionalWindow start. Default: 24 hours ago; reaches at most 90 days back.
untilISO-8601OptionalWindow end. Default: now.
limitintegerOptionalResend at most this many (newest first), 1–100. Default 100. When has_more is true in the response, repeat the same call to drain the rest.

Resend exact deliveries (advanced)

ParameterTypeRequiredDescription
deliveriesarrayOptional1–100 items of { webhook_id, event_id } — for when you already know exactly which deliveries to re-post. Provide either deliveries OR query, never both.
target_webhook_idUUIDOptionalWorks with both modes. Redirect: post every resend to this endpoint of your account instead of each delivery’s original endpoint. Must be an enabled endpoint; the redirect (source endpoint + time) is recorded on the delivery history and in the audit trail.

Eligibility

Only deliveries whose latest attempt is failed are resendable, and only real event deliveries — endpoint-test attempts have no source event and are skipped. Everything else is reported back with a reason: not_failed (latest attempt succeeded or is still retrying), not_found (no such delivery in the last 90 days), or locked (the same delivery was already submitted within the last 60 seconds — duplicate submissions are deduplicated).

Replay markers

Every resent delivery carries two extra request headers so your receiver can deduplicate safely when the original was actually processed before an outage: X-Wemail-Replay: true (present only on manual resends — organic first deliveries and automatic retries never send it) and X-Wemail-Delivery-Id: <event_id> — the source event id, stable across every replay of the same delivery and identical to the event_id you pass to this endpoint. The X-Webhook-Attempt header keeps counting from the delivery’s existing history (a first resend after 3 failed tries arrives as attempt 4).

Retention

Resend reaches up to 90 days back, bounded by your plan’s delivery-history retention: 30 days on Free, 12 months on paid plans — the same window as events and the Console Activity grid.

Responses

StatusDescription
200OK. { results: [{ webhook_id, event_id, accepted, reason }], accepted_count, skipped_count, matched_count, has_more } — per-delivery acceptance detail; has_more (query mode) means more matches exist beyond limit, so repeat the call.
400Validation error — malformed body, or target_webhook_id is not an enabled endpoint of your account.
401Missing or invalid API key.
429Rate limit exceeded (30 calls/minute).

Code examples

cURL
# Resend two failed deliveries, redirected to a backup endpoint
curl -X POST https://api.wemail.io/v3/webhooks/deliveries/resend \
  -H "Authorization: Bearer afn_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "deliveries": [
      { "webhook_id": "9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c", "event_id": "evt_01J8ZQ3K9WVA7M2P" },
      { "webhook_id": "9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c", "event_id": "evt_01J8ZQ4T2XNC8R5D" }
    ],
    "target_webhook_id": "4c1d2e3f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"
  }'
Node.js
// Resend failed deliveries — here redirected to a backup endpoint
// of the same account. Only failed deliveries are eligible.
const { results } = await wemail.webhooks.resendDeliveries({
  deliveries: [
    { webhook_id: "9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c", event_id: "evt_01J8ZQ3K9WVA7M2P" },
    { webhook_id: "9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c", event_id: "evt_01J8ZQ4T2XNC8R5D" },
  ],
  target_webhook_id: "4c1d2e3f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
});
for (const r of results) console.log(r.event_id, r.accepted, r.reason);
Python
result = wemail.webhooks.resend_deliveries(
    deliveries=[
        {"webhook_id": "9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c", "event_id": "evt_01J8ZQ3K9WVA7M2P"},
        {"webhook_id": "9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c", "event_id": "evt_01J8ZQ4T2XNC8R5D"},
    ],
    target_webhook_id="4c1d2e3f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
)
for r in result["results"]:
    print(r["event_id"], r["accepted"], r["reason"])
PHP
$result = $wemail->webhooks->resendDeliveries([
    'deliveries' => [
        ['webhook_id' => '9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c', 'event_id' => 'evt_01J8ZQ3K9WVA7M2P'],
        ['webhook_id' => '9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c', 'event_id' => 'evt_01J8ZQ4T2XNC8R5D'],
    ],
    'target_webhook_id' => '4c1d2e3f-5a6b-4c7d-8e9f-0a1b2c3d4e5f',
]);
Ruby
result = wemail.webhooks.resend_deliveries(
  deliveries: [
    { webhook_id: "9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c", event_id: "evt_01J8ZQ3K9WVA7M2P" },
    { webhook_id: "9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c", event_id: "evt_01J8ZQ4T2XNC8R5D" }
  ],
  target_webhook_id: "4c1d2e3f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"
)
Go
result, _ := client.Webhooks.ResendDeliveries(ctx, &wemail.ResendDeliveriesParams{
    Deliveries: []wemail.DeliveryRef{
        {WebhookID: "9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c", EventID: "evt_01J8ZQ3K9WVA7M2P"},
        {WebhookID: "9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c", EventID: "evt_01J8ZQ4T2XNC8R5D"},
    },
    TargetWebhookID: "4c1d2e3f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
})
Java
JSONObject result = wemail.webhooks().resendDeliveries(
  ResendDeliveriesParams.builder()
    .delivery("9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c", "evt_01J8ZQ3K9WVA7M2P")
    .delivery("9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c", "evt_01J8ZQ4T2XNC8R5D")
    .targetWebhookId("4c1d2e3f-5a6b-4c7d-8e9f-0a1b2c3d4e5f")
    .build());
.NET
var result = await wemail.Webhooks.ResendDeliveriesAsync(new ResendDeliveriesParams {
  Deliveries = new[] {
    new DeliveryRef { WebhookId = "9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c", EventId = "evt_01J8ZQ3K9WVA7M2P" },
    new DeliveryRef { WebhookId = "9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c", EventId = "evt_01J8ZQ4T2XNC8R5D" },
  },
  TargetWebhookId = "4c1d2e3f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
});
ResponseExample response
{
  "results": [
    {
      "webhook_id": "9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c",
      "event_id": "evt_01J8ZQ3K9WVA7M2P",
      "accepted": true,
      "reason": null
    },
    {
      "webhook_id": "9b2f6c1e-4a8d-4f3b-9c7e-2d1a5b8e0f4c",
      "event_id": "evt_01J8ZQ4T2XNC8R5D",
      "accepted": false,
      "reason": "not_failed"
    }
  ]
}