Webhook Ingest
The webhook ingest endpoint receives incoming webhooks from external providers.
Endpoint
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
| Parameter | Description |
|---|---|
orgSlug | Your organization's URL-safe slug |
sourceSlug | The 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/shopifyRequest
Headers
Include headers as sent by your webhook provider. Common headers:
| Header | Description |
|---|---|
Content-Type | Usually application/json |
X-GitHub-Event | GitHub event type |
X-Hub-Signature-256 | GitHub signature |
Stripe-Signature | Stripe signature |
X-Shopify-Topic | Shopify event type |
X-Slack-Signature | Slack signature |
Body
Send the webhook payload as received from the provider. WebhookRelay supports:
application/json- JSON payloadsapplication/x-www-form-urlencoded- Form datatext/plain- Plain text- Any other content type (stored as-is)
Response
Success (200 OK)
{
"eventId": "evt_abc123",
"status": "received"
}Validation Failed (400 Bad Request)
Signature verification failed:
{
"error": "Bad Request",
"message": "Invalid signature",
"code": "INVALID_SIGNATURE"
}Source Not Found (404 Not Found)
{
"error": "Not Found",
"message": "Source not found",
"code": "SOURCE_NOT_FOUND"
}Source Disabled (403 Forbidden)
{
"error": "Forbidden",
"message": "Source is disabled",
"code": "SOURCE_DISABLED"
}Examples
GitHub Webhook
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
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
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
- Navigate to Testing in the sidebar
- Select your source
- Enter a test payload
- Click Send Test
Using cURL
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:
{
"verificationConfig": {
"type": "none"
}
}WARNING
Never disable verification in production!
Payload Size Limits
| Plan | Max Payload Size |
|---|---|
| Free | 256 KB |
| Starter | 1 MB |
| Pro | 5 MB |
| Business | 10 MB |
| Enterprise | Custom |
Payloads exceeding the limit return:
{
"error": "Payload Too Large",
"message": "Payload size exceeds limit of 1MB",
"code": "PAYLOAD_TOO_LARGE"
}Rate Limits
Ingest endpoints are rate limited per source:
| Plan | Requests/second |
|---|---|
| Free | 10 |
| Starter | 100 |
| Pro | 500 |
| Business | 2000 |
| Enterprise | Custom |
Rate limited requests return:
{
"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:
- First request: Creates new event
- Second request: Returns existing event ID
Response for duplicate:
{
"eventId": "evt_abc123",
"status": "duplicate"
}Best Practices
Configure verification: Always enable signature verification for security
Handle responses: Check response status and log failures
Implement idempotency: Your handler should handle duplicate deliveries
Use HTTPS: Always use the HTTPS endpoint
Monitor ingest: Check the dashboard for failed verifications
Troubleshooting
Signature Verification Failing
- Verify the secret matches what you configured
- Check if the provider is sending the expected header
- Ensure the raw body isn't modified before verification
- Check timestamp tolerance for providers that use it
Webhooks Not Arriving
- Verify the URL is correct (org slug + source slug)
- Check if the source is enabled
- Verify network connectivity from the provider
- Check provider's webhook logs for errors