Forwarding Webhooks

Forwarding is what makes webhook.rodeo powerful—we receive webhooks, capture them for debugging, and then reliably forward them to your application with automatic retries and full delivery tracking.

How forwarding works

When you configure a forward URL on your webhook, every incoming event follows this flow:

  1. Event received - webhook.rodeo captures the incoming webhook
  2. Event logged - Complete request data saved to database
  3. Forwarding initiated - We send the webhook to your URL
  4. Response captured - Status, headers, body, and latency recorded
  5. Retries on failure - Automatic retries if forwarding fails
  6. Final status - Success or failure after all attempts

All of this happens asynchronously using durable workflows, so webhooks will be reliably delivered.

Headers preserved

When forwarding, we preserve all original headers from the sending service and add a few of our own:

POST https://your-app.com/api/webhooks
Content-Type: application/json
X-GitHub-Event: push
X-Rodeo-Request-Id: req_abc123
X-Rodeo-Webhook-Id: webhook_xyz789
X-Rodeo-Attempt: 1

See our Headers Reference for details on special headers we add.

Automatic retries

When forwarding fails (network error, 5xx response, timeout), webhook.rodeo automatically retries with exponential backoff:

AttemptTimingDelay
1Immediate0s
2After failure30s
3After failure2 minutes
4After failure10 minutes

What counts as a failure?

Forwarding is considered failed when:

  • Network errors - DNS failure, connection timeout, connection refused

  • 5xx responses - Server errors (500, 502, 503, 504)

  • Timeouts - No response within 30 seconds

  • 4xx status codes - 400, 404, 422, etc. (client errors don't retry)

What counts as success?

Forwarding succeeds when your endpoint returns:

  • 2xx status codes - 200, 201, 202, 204, etc.

Retry behavior example

Let's say your endpoint is temporarily down:

10:00:00 - Attempt 1: Connection refused ❌
10:00:30 - Attempt 2: Connection refused ❌ (30s later)
10:02:30 - Attempt 3: Connection refused ❌ (2m later)
10:12:30 - Attempt 4: 200 OK ✅ (10m later)

Your endpoint came back online before the final retry, and the webhook was successfully delivered. All four attempts are logged for your review.

Delivery tracking

Every forward attempt creates a delivery record with complete details:

{
  "id": "delivery_123",
  "event_id": "evt_abc",
  "attempt_number": 2,
  "succeeded": true,
  "request_headers": {
    "content-type": "application/json",
    "x-rodeo-request-id": "req_xyz",
    "x-rodeo-attempt": "2"
  },
  "request_body": "{...}",
  "response_status": 200,
  "response_headers": {
    "content-type": "application/json",
    "x-request-id": "your-app-request-id"
  },
  "response_body": "{\"status\":\"processed\"}",
  "response_latency_ms": 145,
  "created_at": "2024-01-15T10:00:30Z"
}

Viewing delivery attempts

In the event detail page, the Delivery section shows:

  • All attempts in chronological order
  • Status badge (Success/Failed)
  • Response status code
  • Latency in milliseconds
  • Expand to see full response headers and body

This gives you complete visibility into what happened when webhook.rodeo tried to forward to your endpoint.

Response inspection

webhook.rodeo captures up to 10KB of response body from your endpoint. This lets you see:

  • Success messages
  • Error details
  • Validation errors
  • Processing status

For example, if your endpoint returns:

{
  "status": "processed",
  "order_id": "ord_12345",
  "message": "Webhook received and order updated"
}

You'll see this in the delivery details, confirming your endpoint processed the webhook correctly.

SSRF protection

Server-Side Request Forgery (SSRF) protection prevents malicious users from using webhook.rodeo to probe internal networks or attack other services.

Blocked destinations

webhook.rodeo blocks forwarding to:

  • Localhost - 127.0.0.1, localhost, ::1
  • Private networks - 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
  • Link-local - 169.254.0.0/16
  • Reserved IPs - 0.0.0.0/8, 240.0.0.0/4

If you try to use a blocked IP, you'll see:

{
  "error": "Invalid forward URL",
  "reason": "SSRF Protection: Private IP addresses are not allowed"
}

Allowed destinations

Any publicly routable HTTPS endpoint is allowed:

  • Your production API
  • Public webhook testing services
  • Cloud function endpoints

Timeouts

Forward requests have a 30-second timeout. If your endpoint doesn't respond within 30 seconds, the request is aborted and recorded as failed.

Why 30 seconds?

Most webhooks should process in under 1 second. A 30-second timeout is generous and prevents:

  • Hanging connections
  • Resource exhaustion
  • Cascading failures

Handling slow endpoints

If your endpoint needs more than 30 seconds to process webhooks:

  1. Return quickly - Respond with 202 Accepted immediately
  2. Process asynchronously - Queue the work for background processing
  3. Use a callback - Have your app notify the original sender when done

This is the recommended pattern for webhook processing anyway—webhook senders expect fast responses.

Was this page helpful?