Skip to content

Webhook Ingest

The webhook ingest endpoint receives incoming webhooks from external providers.

Endpoint

http
POST /ingest/{orgSlug}/{sourceSlug}

This endpoint is public and does not require authentication. Security is handled through signature verification configured on the source.

URL Structure

ParameterDescription
orgSlugYour organization's URL-safe slug
sourceSlugThe source's URL-safe slug

Example URLs:

https://api.webhookrelay.com/ingest/my-company/github
https://api.webhookrelay.com/ingest/my-company/stripe-prod
https://api.webhookrelay.com/ingest/acme-corp/shopify

Request

Headers

Include headers as sent by your webhook provider. Common headers:

HeaderDescription
Content-TypeUsually application/json
X-GitHub-EventGitHub event type
X-Hub-Signature-256GitHub signature
Stripe-SignatureStripe signature
X-Shopify-TopicShopify event type
X-Slack-SignatureSlack signature

Body

Send the webhook payload as received from the provider. WebhookRelay supports:

  • application/json - JSON payloads
  • application/x-www-form-urlencoded - Form data
  • text/plain - Plain text
  • Any other content type (stored as-is)

Response

Success (200 OK)

json
{
  "eventId": "evt_abc123",
  "status": "received"
}

Validation Failed (400 Bad Request)

Signature verification failed:

json
{
  "error": "Bad Request",
  "message": "Invalid signature",
  "code": "INVALID_SIGNATURE"
}

Source Not Found (404 Not Found)

json
{
  "error": "Not Found",
  "message": "Source not found",
  "code": "SOURCE_NOT_FOUND"
}

Source Disabled (403 Forbidden)

json
{
  "error": "Forbidden",
  "message": "Source is disabled",
  "code": "SOURCE_DISABLED"
}

Examples

GitHub Webhook

bash
curl -X POST https://api.webhookrelay.com/ingest/my-company/github \
  -H "Content-Type: application/json" \
  -H "X-GitHub-Event: push" \
  -H "X-GitHub-Delivery: 72d3162e-cc78-11e3-81ab-4c9367dc0958" \
  -H "X-Hub-Signature-256: sha256=abc123..." \
  -d '{
    "ref": "refs/heads/main",
    "repository": {"full_name": "user/repo"},
    "pusher": {"name": "user"}
  }'

Stripe Webhook

bash
curl -X POST https://api.webhookrelay.com/ingest/my-company/stripe \
  -H "Content-Type: application/json" \
  -H "Stripe-Signature: t=1492774577,v1=abc123..." \
  -d '{
    "id": "evt_123",
    "type": "payment_intent.succeeded",
    "data": {"object": {...}}
  }'

Custom Webhook

bash
curl -X POST https://api.webhookrelay.com/ingest/my-company/custom \
  -H "Content-Type: application/json" \
  -H "X-Custom-Header: value" \
  -d '{
    "event": "order.created",
    "data": {"orderId": "12345"}
  }'

Signature Verification

When a source has signature verification configured, the ingest endpoint validates incoming webhooks.

GitHub

Verifies X-Hub-Signature-256 header using HMAC-SHA256:

X-Hub-Signature-256: sha256=<signature>

Stripe

Verifies Stripe-Signature header with timestamp and signature:

Stripe-Signature: t=<timestamp>,v1=<signature>

Slack

Verifies X-Slack-Signature with timestamp:

X-Slack-Request-Timestamp: <timestamp>
X-Slack-Signature: v0=<signature>

Generic HMAC

Verifies custom header with configurable algorithm:

X-Your-Signature: sha256=<signature>

Testing Webhooks

Using the Dashboard

  1. Navigate to Testing in the sidebar
  2. Select your source
  3. Enter a test payload
  4. Click Send Test

Using cURL

bash
curl -X POST https://api.webhookrelay.com/ingest/my-company/test \
  -H "Content-Type: application/json" \
  -d '{"test": true}'

Disable Verification for Testing

For development, you can set verification to none:

json
{
  "verificationConfig": {
    "type": "none"
  }
}

WARNING

Never disable verification in production!

Payload Size Limits

PlanMax Payload Size
Free256 KB
Starter1 MB
Pro5 MB
Business10 MB
EnterpriseCustom

Payloads exceeding the limit return:

json
{
  "error": "Payload Too Large",
  "message": "Payload size exceeds limit of 1MB",
  "code": "PAYLOAD_TOO_LARGE"
}

Rate Limits

Ingest endpoints are rate limited per source:

PlanRequests/second
Free10
Starter100
Pro500
Business2000
EnterpriseCustom

Rate limited requests return:

json
{
  "error": "Too Many Requests",
  "message": "Rate limit exceeded",
  "code": "RATE_LIMITED",
  "retryAfter": 1
}

Idempotency

WebhookRelay deduplicates webhooks based on payload hash within a 5-minute window. If you send the exact same payload twice:

  1. First request: Creates new event
  2. Second request: Returns existing event ID

Response for duplicate:

json
{
  "eventId": "evt_abc123",
  "status": "duplicate"
}

Best Practices

  1. Configure verification: Always enable signature verification for security

  2. Handle responses: Check response status and log failures

  3. Implement idempotency: Your handler should handle duplicate deliveries

  4. Use HTTPS: Always use the HTTPS endpoint

  5. Monitor ingest: Check the dashboard for failed verifications

Troubleshooting

Signature Verification Failing

  1. Verify the secret matches what you configured
  2. Check if the provider is sending the expected header
  3. Ensure the raw body isn't modified before verification
  4. Check timestamp tolerance for providers that use it

Webhooks Not Arriving

  1. Verify the URL is correct (org slug + source slug)
  2. Check if the source is enabled
  3. Verify network connectivity from the provider
  4. Check provider's webhook logs for errors

Released under the MIT License.