Webhooks

Retries & Delivery Logs

How BeeL. retries failed deliveries and how to inspect the delivery history.


BeeL. automatically retries webhook deliveries that fail due to network errors or non-2xx HTTP responses. You can also inspect the delivery history and trigger manual retries from the dashboard or the API.

Retry Policy

ParameterValue
Max attempts5 (1 original + 4 automatic retries)
Retry strategyExponential backoff
Initial delay5 seconds
Max delay6 hours
Timeout per attempt10 seconds

Backoff Schedule

AttemptApproximate delay after previous attempt
1st (original)
2nd~5 seconds
3rd~10 seconds
4th~20 seconds
5th~40 seconds

If all 5 attempts fail, no further automatic retries occur. You can still trigger a manual retry from the dashboard or API at any time.

What Triggers a Retry

ScenarioRetried?
Your server returns 5xx✅ Yes
Connection refused / DNS failure / timeout✅ Yes
Your server returns 4xx❌ No — treated as a client error
Your server returns 2xx❌ No — delivery successful

If your endpoint returns 4xx, BeeL. assumes the payload is invalid and will not retry. Return 5xx if you need BeeL. to retry (e.g. your database is temporarily unavailable).

Delivery Logs

BeeL. keeps a log of the last 50 delivery attempts per subscription. Each log entry records:

  • Delivery timestamp and duration
  • HTTP status code and response body
  • All request headers sent (including BeeL-Signature, BeeL-Event-Id, BeeL-Delivery-Id, BeeL-Event)
  • The full JSON payload that was sent
  • The attempt number
  • Error description (for failed deliveries)

View delivery logs

curl "https://app.beel.es/api/v1/webhooks/{webhook_id}/deliveries" \
  -H "Authorization: Bearer beel_sk_live_xxx"
{
  "success": true,
  "data": [
    {
      "id": "8ee6b023-c4e5-482e-93ca-dc66da2f9cb5",
      "subscription_id": "3f7a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
      "event_type": "verifactu.status.updated",
      "attempt_number": 1,
      "http_status": 200,
      "success": true,
      "duration_ms": 142,
      "response_body": "OK",
      "error_message": null,
      "request_headers": {
        "BeeL-Signature": "t=1741362026,v1=3c4f7a2e...",
        "BeeL-Event": "verifactu.status.updated",
        "BeeL-Event-Id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "BeeL-Delivery-Id": "8ee6b023-c4e5-482e-93ca-dc66da2f9cb5",
        "Idempotency-Key": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
      },
      "payload": "{\"id\":\"a1b2c3d4...\",\"type\":\"verifactu.status.updated\",...}",
      "delivered_at": "2026-03-07T16:20:26Z"
    }
  ]
}

Log fields

FieldDescription
idUnique ID for this delivery attempt. Matches the BeeL-Delivery-Id header.
attempt_number1 for the original delivery, 2 for the first retry, etc.
http_statusHTTP status returned by your endpoint. null if connection failed.
successtrue if your endpoint returned 2xx
duration_msRound-trip time in milliseconds
error_messageError detail for failed attempts (timeout, DNS error, etc.)
request_headersHeaders sent with this attempt — useful for re-verifying the signature
payloadThe exact JSON body that was sent to your endpoint

Manual Retry

Trigger a retry for any delivery — successful or not:

curl -X POST \
  "https://app.beel.es/api/v1/webhooks/{webhook_id}/deliveries/{delivery_id}/retry" \
  -H "Authorization: Bearer beel_sk_live_xxx"

This uses the original payload and generates a new signature with a fresh timestamp. A new delivery log entry is created with the result.

Endpoint Requirements

To ensure reliable delivery, your endpoint should:

  • Respond within 10 seconds — BeeL. does not wait longer
  • Return 2xx immediately and process the event asynchronously if needed
  • Return 5xx if you are temporarily unable to process (triggers a retry)
  • Not return 4xx unless the payload itself is the problem
// ✅ Good: acknowledge immediately, process async
app.post('/webhooks/beel', async (req, res) => {
  res.status(200).send('OK'); // respond first
  await queue.push(req.body); // process later
});

// ❌ Bad: synchronous processing blocks the response
app.post('/webhooks/beel', async (req, res) => {
  await processEventSynchronously(req.body); // may exceed 10s timeout
  res.status(200).send('OK');
});