Custom Webhooks
Integrate a provider Hookbase has no preset for, using the generic custom HMAC scheme — hex HMAC-SHA256 of the raw body, read from X-Signature, X-Webhook-Signature or X-Hub-Signature-256, with or without a sha256= prefix. Those three header names and that one algorithm are fixed; they are not configurable per source.
When to Use Custom Integration
Use custom webhooks when:
- Your provider isn't specifically supported
- You're building your own webhook sender
- You need custom signature verification
- You want to receive webhooks without verification (development only)
Generic HMAC Verification
Most webhook providers use HMAC signatures. Configure generic HMAC verification:
curl -X POST https://api.hookbase.app/api/sources \
-H "Authorization: Bearer whr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Custom Webhooks",
"slug": "custom",
"provider": "custom",
"signingSecret": "your-webhook-secret"
}'Configuration Options
| Option | Description | Values |
|---|---|---|
algorithm | Hash algorithm | sha1, sha256, sha512 |
header | Header containing signature | Any header name |
encoding | Signature encoding | hex, base64 |
prefix | Prefix to strip from signature | e.g., sha256=, v1= |
Common Provider Configurations
Shopify
{
"provider": "shopify",
"signingSecret": "your-shopify-client-secret"
}Twilio
{
"provider": "twilio",
"signingSecret": "your-auth-token"
}SendGrid
SendGrid signs with ECDSA, which the custom HMAC scheme cannot check, but Hookbase verifies it
natively under the sendgrid provider. Paste SendGrid's public key (from the Event Webhook's
Signature verification settings) as the source's signingSecret:
{
"provider": "sendgrid",
"signingSecret": "your-sendgrid-public-key"
}See the SendGrid guide.
Linear
Linear's Linear-Signature header is not one of the three custom reads, but Hookbase verifies it
natively under the linear provider. Paste the webhook's Signing secret from Linear:
{
"provider": "linear",
"signingSecret": "your-linear-signing-secret"
}See the Linear guide.
Paddle
{
"provider": "paddle",
"signingSecret": "your-paddle-secret-key"
}No Verification
For development or trusted internal services:
{
}Danger
Never use type: none in production! This allows anyone to send webhooks to your endpoint.
API Key Authentication
Some providers authenticate via headers. Add custom headers to your destinations:
{
"name": "Internal Service",
"url": "https://internal.example.com/webhook",
"headers": {
"X-API-Key": "your-api-key",
"Authorization": "Bearer your-token"
}
}Building Your Own Webhook Sender
Signing Webhooks
When building a system that sends webhooks, implement HMAC signing:
const crypto = require('crypto');
function signPayload(payload, secret) {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(JSON.stringify(payload));
return 'sha256=' + hmac.digest('hex');
}
// Send webhook
const payload = { event: 'user.created', data: { id: '123' } };
const signature = signPayload(payload, 'your-secret');
fetch('https://api.hookbase.app/ingest/myorg/custom', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Signature': signature
},
body: JSON.stringify(payload)
});Python Example
import hmac
import hashlib
import json
import requests
def sign_payload(payload, secret):
message = json.dumps(payload).encode()
signature = hmac.new(
secret.encode(),
message,
hashlib.sha256
).hexdigest()
return f'sha256={signature}'
payload = {'event': 'user.created', 'data': {'id': '123'}}
signature = sign_payload(payload, 'your-secret')
requests.post(
'https://api.hookbase.app/ingest/myorg/custom',
json=payload,
headers={
'X-Signature': signature
}
)Transform Examples
Generic Event Normalizer
function transform(payload) {
// Normalize different webhook formats to a standard structure
return {
type: payload.event || payload.type || payload.action || 'unknown',
data: payload.data || payload.payload || payload,
timestamp: payload.timestamp || payload.created_at || new Date().toISOString(),
source: 'custom'
};
}Extract Common Fields
function transform(payload) {
// Extract user info from various formats
const user = payload.user || payload.data?.user || payload.customer || {};
return {
event: payload.event,
userId: user.id || user.user_id || user.uid,
userEmail: user.email || user.email_address,
userName: user.name || user.display_name || user.username,
metadata: payload.metadata || {},
receivedAt: new Date().toISOString()
};
}Webhook to Slack
function transform(payload) {
return {
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: `*Webhook Received*\n\`\`\`${JSON.stringify(payload, null, 2)}\`\`\``
}
}
]
};
}Filter Examples
By Event Type
{
"name": "User Events",
"logic": "AND",
"conditions": [
{
"field": "event",
"operator": "starts_with",
"value": "user."
}
]
}By Environment
{
"name": "Production Only",
"logic": "AND",
"conditions": [
{
"field": "environment",
"operator": "equals",
"value": "production"
}
]
}Has Required Fields
{
"name": "Valid Payload",
"logic": "AND",
"conditions": [
{
"field": "event",
"operator": "exists",
"value": ""
},
{
"field": "data.id",
"operator": "exists",
"value": ""
}
]
}Testing Custom Webhooks
Using cURL
# Without signature (for testing with verification disabled)
curl -X POST https://api.hookbase.app/ingest/myorg/custom \
-H "Content-Type: application/json" \
-d '{"event": "test", "data": {"message": "Hello!"}}'
# With HMAC signature
PAYLOAD='{"event": "test", "data": {"message": "Hello!"}}'
SECRET="your-secret"
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print "sha256="$2}')
curl -X POST https://api.hookbase.app/ingest/myorg/custom \
-H "Content-Type: application/json" \
-H "X-Signature: $SIGNATURE" \
-d "$PAYLOAD"Using the Dashboard
- Navigate to Testing
- Select your custom source
- Enter a test payload
- Click Send Test
Troubleshooting
Signature Verification Failed
- Verify the algorithm matches your provider's implementation
- Check the header name is correct (case-sensitive)
- Ensure encoding matches (hex vs base64)
- Verify the prefix is stripped correctly
- Check if the payload is being stringified consistently
Wrong Signature Format
Common issues:
- Using base64 when provider uses hex (or vice versa)
- Missing or wrong prefix
- Wrong hash algorithm
- Extra whitespace in the signature
Payload Not Matching
Ensure you're signing the exact same bytes that are sent:
- Same JSON serialization (key order, spacing)
- Same character encoding (UTF-8)
- No modifications by proxies or middleware