Retries and rate limits

How failed deliveries are tried again, how to limit how fast we send to your app, and how to send failed deliveries again.

When your app does not take a delivery, we try again, with a wait that grows each time, until the destination’s last attempt. A rate limit keeps us from sending faster than your app can take.

What counts as a failure

An attempt succeeds when your app answers with a 2xx status. Anything else fails it:

lastErrorClassMeans
httpYour app answered with a status that is not 2xx. lastStatusCode holds it
redirectYour app answered with a redirect, which is not followed
timeoutYour app did not answer within the destination’s timeoutSeconds
dnsThe address’s name could not be found
tlsThe secure connection could not be made, such as with an expired certificate
connectThe connection was refused or dropped
blockedThe address is on a private network, so it is never called
requestThe request could not be sent to that address
lostThe attempt was cut short on our side. It is tried again straight away
transformationThe connection’s transformation failed. It is not tried again
cancelledThe delivery was cancelled before it was sent

When we try again

After a failed attempt, the next one waits 30 seconds, then twice as long each time, up to an hour:

After attemptNext attempt
130 seconds later
21 minute later
32 minutes later
44 minutes later
58 minutes later
616 minutes later
732 minutes later
8 and later1 hour later

When your app answers 429 or 503 with a Retry-After header, we wait that long instead when it is longer, up to an hour.

A destination makes 6 attempts by default. Set maxAttempts, from 1 to 20, to change it. After the last attempt, the delivery is failed.

Limit how fast we send

A rate limit caps how many attempts a destination gets in a second, a minute, or an hour:

{ "rateLimit": { "count": 50, "period": "minute" } }

Deliveries over the limit wait their turn; nothing is dropped. At most 5 attempts are in flight to one destination at once, so one slow destination never holds up the others. Leave rateLimit out, or set it to null, for no limit.

Send failed deliveries again

Sending a delivery again makes a new delivery of the same event to the same destination, sent straight away with a full set of attempts. It keeps the same webhook-id, so your app can tell it has seen the event.

  • One delivery: POST /v1/projects/{project}/deliveries/{delivery}/resend
  • Up to 100 you choose: POST /v1/projects/{project}/deliveries/resend with their IDs in deliveryIds
  • Every failed delivery, of one destination or the whole project: POST /v1/projects/{project}/deliveries/resend-failed. It skips a delivery that was already sent again, and sends up to 1,000 at a time.
curl -X POST "$HOOKS_URL/deliveries/resend-failed" \
  -H "Authorization: Bearer $HOOKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{ \"destinationId\": \"$DESTINATION_ID\" }"

The answer says how many were sent again, as resent. In the console, select deliveries and choose Send again.

Cancel a waiting delivery

A delivery that is pending or retrying can be cancelled, so it is never sent:

curl -X POST "$HOOKS_URL/deliveries/$DELIVERY_ID/cancel" \
  -H "Authorization: Bearer $HOOKS_API_KEY"

A delivery that is being sent, or has finished, gets 409 with the code delivery_not_waiting.

Last updated