Production Readiness
Before launching your webhook pipeline to production, ensure your configuration follows security best practices and reliability standards. This guide provides a comprehensive checklist and detailed recommendations for running Hookbase securely and reliably.
Pre-Launch Checklist
Use this checklist to verify your production configuration:
| Category | Item | Status | Notes |
|---|---|---|---|
| Security | Signature verification enabled for all sources | ⬜ | See Signature Verification |
| API key scopes limited to minimum required | ⬜ | Use read:events instead of admin where possible | |
| HTTPS-only destinations configured | ⬜ | Never send webhooks to http:// endpoints | |
| IP filtering configured for sensitive sources | ⬜ | Allowlist provider IPs where available | |
| Field encryption enabled for PII fields | ⬜ | Encrypt sensitive data like emails, SSNs | |
rejectInvalidSignatures enabled on sources | ⬜ | Prevents processing of unverified webhooks | |
| Reliability | Retry policies configured with backoff | ⬜ | Recommended: 3-5 retries with exponential backoff |
| Failover destinations set for critical routes | ⬜ | Secondary destination for automatic failover | |
| Circuit breakers enabled on destinations | ⬜ | Prevents cascading failures | |
| Deduplication enabled to prevent duplicates | ⬜ | Use idempotencyKey or content-based dedup | |
| Notification channels linked to routes | ⬜ | Get alerted on delivery failures | |
| Cron monitoring active for scheduled jobs | ⬜ | Monitor cron execution success rates | |
| Monitoring | Alert channels configured for critical routes | ⬜ | Email, Slack, or webhook notifications |
| Audit logs reviewed periodically | ⬜ | Check for unauthorized access attempts | |
| Analytics dashboard checked for anomalies | ⬜ | Monitor event volume and failure rates | |
| Health endpoint monitored externally | ⬜ | Use uptime monitoring service | |
| Access Control | Organization roles assigned appropriately | ⬜ | Admin, Developer, Viewer roles |
| API key rotation scheduled (quarterly) | ⬜ | Rotate keys every 90 days minimum | |
| Unused API keys revoked | ⬜ | Remove keys for deactivated services | |
| Least privilege principle applied | ⬜ | Grant minimum scopes needed |
Signature Verification
Signature verification is the most critical security control for webhooks. It ensures that incoming webhooks are authentic and haven't been tampered with.
Why It Matters
Without signature verification:
- Attackers can send fake webhooks to trigger unauthorized actions
- Replay attacks can resubmit legitimate webhooks
- Man-in-the-middle attacks can modify webhook payloads
Enabling Verification
When creating a source, always enable signature verification:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/sources \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Stripe Payments",
"provider": "stripe",
"verifySignatures": true,
"rejectInvalidSignatures": true,
"signingSecret": "whsec_abc123..."
}'Provider-Specific Configuration
Each provider has different signature verification mechanisms:
| Provider | Header | Algorithm | Configuration |
|---|---|---|---|
| Stripe | Stripe-Signature | HMAC-SHA256 | Signing secret from webhook settings |
| GitHub | X-Hub-Signature-256 | HMAC-SHA256 | Secret token from webhook config |
| Slack | X-Slack-Signature | HMAC-SHA256 | Signing secret from app credentials |
| Shopify | X-Shopify-Hmac-SHA256 | HMAC-SHA256 | Shared secret from admin panel |
| Twilio | X-Twilio-Signature | HMAC-SHA1 | Auth token from console |
Reject vs. Accept Invalid Signatures
The rejectInvalidSignatures setting controls behavior when verification fails:
rejectInvalidSignatures: true (Recommended for Production)
- Invalid webhooks return
401 Unauthorized - Event is NOT stored in Hookbase
- No deliveries are attempted
- Audit log records the rejection
rejectInvalidSignatures: false (Development/Testing Only)
- Invalid webhooks return
200 OK - Event is stored with
verificationStatus: "failed" - Deliveries are still attempted (not recommended)
- Useful for debugging signature issues
Warning
Production Requirement
Always set rejectInvalidSignatures: true in production. Accepting unverified webhooks defeats the purpose of signature verification and exposes your system to attacks.
Secret & Key Rotation
Regular rotation of secrets and API keys is essential for security hygiene.
API Key Rotation
Rotate API keys on a regular schedule (recommended: every 90 days):
Step 1: Create new API key
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/api-keys \
-H "Authorization: Bearer CURRENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Production API Key (2026-Q2)",
"scopes": ["read:sources", "read:events", "write:routes"],
"expiresAt": "2026-06-01T00:00:00Z"
}'Step 2: Update services to use new key
- Deploy new key to all services that consume your webhooks
- Verify services are working with new key
- Monitor for authentication errors
Step 3: Revoke old key
curl -X DELETE https://api.hookbase.app/api/organizations/{orgId}/api-keys/{keyId} \
-H "Authorization: Bearer NEW_API_KEY"Tip
Zero-Downtime Rotation
Set expiresAt on the old key to 7 days in the future instead of immediately revoking it. This allows a grace period for services to migrate.
Source Signing Secret Rotation
For webhook sources, rotate signing secrets using the built-in rotation mechanism:
# Via CLI
hookbase sources rotate-secret 11111111-1111-4111-8111-111111111111 --org myorg
# Via API
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/sources/{sourceId}/rotate-secret \
-H "Authorization: Bearer YOUR_API_KEY"This returns a new signingSecret. Update it in your webhook provider:
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "Stripe Payments",
"signingSecret": "whsec_NEW_SECRET",
"previousSecret": "whsec_OLD_SECRET",
"secretRotatedAt": "2026-02-11T10:30:00Z"
}Tip
Graceful Migration
Hookbase keeps the previous secret active for 24 hours, allowing webhooks signed with either the old or new secret to pass verification. This prevents dropped webhooks during rotation.
Zero-Downtime Rotation Strategy
For critical production systems:
- Pre-rotate: Create new secret but don't activate yet
- Update provider: Configure webhook provider with new secret (but don't delete old config yet)
- Activate: Rotate secret in Hookbase (both secrets now valid for 24h)
- Monitor: Watch delivery success rates for any verification failures
- Clean up: After 24h, remove old secret from provider configuration
Replay Prevention
Replay attacks involve resubmitting valid webhooks to trigger duplicate actions (e.g., double-charging a customer).
Deduplication
Enable deduplication to automatically reject duplicate webhooks:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/sources \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Stripe Payments",
"slug": "stripe-payments",
"provider": "stripe",
"dedupEnabled": true,
"dedupStrategy": "auto",
"dedupWindowHours": 24
}'Deduplication Strategies:
dedupStrategy | Key used |
|---|---|
auto | The provider's event ID when Hookbase can extract one, otherwise the payload hash. The default, and the right answer for almost every source |
provider_id | The provider's event ID only. An event without one is not deduplicated |
idempotency_key | Same key as provider_id — a custom header, X-Idempotency-Key, or Idempotency-Key take priority over the provider's own id in both |
payload_hash | SHA-256 of the payload body. Catches byte-identical redeliveries only |
none | Deduplication off |
There is no field path to configure: the event ID is read from the place each provider puts it —
Stripe's payload id, GitHub's X-GitHub-Delivery, Shopify's X-Shopify-Webhook-Id, and so on. To
dedupe on a header of your own instead, name it in dedupCustomHeader.
dedupWindowHours accepts 1 to 168 (seven days).
Timestamp Checking
Many providers include a timestamp in their signature. Hookbase rejects webhooks whose timestamp falls outside that provider's tolerance window, which prevents replay of a captured request.
The tolerance is part of each provider's signature scheme, not a source setting — there is no
timestampTolerance field to send. Stripe's is 300 seconds; a provider whose scheme does not sign a
timestamp has no window to check. Selecting the right provider on the source is what turns this on.
Idempotency Keys in Destinations
For critical operations, ensure your destination endpoints support idempotency keys:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/transforms \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Add Idempotency Header",
"type": "jsonata",
"expression": "{\"headers\": {\"Idempotency-Key\": $.event.id}}"
}'Attach this transform to routes delivering to payment processors or other systems where duplicate requests could cause problems.
Network Security
Control which traffic can reach your webhook sources and where deliveries can be sent.
IP Filtering
Restrict webhook sources to accept only traffic from known provider IPs:
curl -X PATCH https://api.hookbase.app/api/organizations/{orgId}/sources/{sourceId} \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"ipFilterMode": "allowlist",
"ipAllowlist": [
"3.18.12.0/22",
"3.130.192.0/22"
]
}'ipFilterMode is one of none, allowlist, denylist, or both. Each entry is a bare IPv4 or
IPv6 address or CIDR string — there is no per-entry description field, so keep the annotated list in
your own infrastructure code. Setting both checks the denylist first.
Warning
IP filtering is a paid-plan feature. Sending ipFilterMode, ipAllowlist, or ipDenylist on a
plan without it returns 403.
Provider IP Ranges:
| Provider | IP Ranges | Documentation |
|---|---|---|
| Stripe | Multiple /22 blocks | https://stripe.com/docs/ips |
| GitHub | https://api.github.com/meta | https://docs.github.com/webhooks/webhook-events-and-payloads#ip-addresses |
| Slack | Multiple /24 blocks | https://api.slack.com/changelog/2018-05-01-webhook-ip-ranges |
| Shopify | Multiple /22 blocks | https://shopify.dev/docs/apps/webhooks |
Warning
Dynamic IP Ranges
Some providers change their IP ranges. Subscribe to their change notifications and update your allowlist accordingly. Consider using denylist mode if allowlisting is not feasible.
HTTPS-Only Enforcement
Never send webhooks to unencrypted HTTP endpoints. Use an https:// URL when you create the
destination:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/destinations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Payment Processor",
"slug": "payment-processor",
"url": "https://api.example.com/webhooks"
}'Warning
There is no requireHttps option. The create and update schemas have no such field, and
because neither is strict, sending it returns a 200 with the key silently discarded — so a
destination you believed was HTTPS-locked is not. http:// URLs are accepted. Enforcing HTTPS
means using an https:// URL here and auditing the ones you already have.
Destination URLs are validated on create and update, but for SSRF rather than for encryption. A URL is rejected when it:
- uses a scheme other than
http:orhttps: - resolves to a private, loopback, link-local or otherwise internal host
- embeds credentials, as in
https://user:pass@host/path - uses a port outside
80,443,8080,8443,3000,4000,5000,8000and9000
To confirm what a destination is really pointed at, read it back:
curl https://api.hookbase.app/api/destinations/{destinationId} \
-H "Authorization: Bearer YOUR_API_KEY"Custom Domains with SSL
For inbound webhooks, use custom domains with SSL certificates:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/custom-domains \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"domain": "webhooks.example.com",
"sslMode": "full_strict"
}'This allows webhooks at https://webhooks.example.com/receive/{sourceSlug} with your own SSL certificate, improving trust and branding.
Data Protection
Protect sensitive data in webhook payloads both at rest and in transit.
Field Encryption
Encrypt PII fields before storing or forwarding webhooks:
Field encryption is a property of the source, set with encryptFields on create or update. There is
no separate endpoint and no per-field algorithm to pick — everything uses AES-256-GCM.
curl -X PATCH https://api.hookbase.app/api/organizations/{orgId}/sources/{sourceId} \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"encryptFields": [
"$.customer.email",
"$.customer.ssn",
"$.payment.cardNumber"
]
}'Warning
Every path must start with $. — $.customer.email, not customer.email. A path without the
prefix is accepted by the API and then matches nothing at ingest, so the field is stored in the
clear with no error to tell you. Array indexes use brackets: $.items[0].email.
Encrypted fields are replaced in the stored payload with an ENC: prefix followed by the
ciphertext:
{
"customer": {
"email": "ENC:qm9YbGF0ZWQtY2lwaGVydGV4dA==",
"name": "John Doe"
}
}A field that is absent or null in a given payload is skipped rather than failing the event.
Warning
Field encryption is a paid-plan feature. Sending encryptFields on a plan without it returns
403.
Tip
Encryption Key Management
Each organization's field-encryption key is derived per request with HKDF-SHA256 from a platform master secret and the organization ID, so no organization can read another's fields and the per-organization key is never stored anywhere. The master secret is a Cloudflare Worker secret, not a KV value, and rotating it re-derives every organization's key — which makes previously stored ciphertext unreadable, so it is not done on a schedule. For compliance requirements (HIPAA, PCI-DSS) that need customer-managed keys, contact support.
Field Masking
For non-encrypted fields that shouldn't appear in logs:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/sources \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Payment Events",
"maskedFields": [
"$.customer.email",
"$.payment.last4",
"$.customer.phone"
]
}'Masked fields appear as *** in audit logs and the dashboard but are delivered normally to destinations.
Payload Retention Policies
Retention is determined by your plan and is not configurable per organization. A scheduled retention job reads each organization's plan, works out a single cutoff date from that plan's retention window, and deletes everything older than it: events, their stored payloads in R2, delivery attempts, tunnel requests and cron execution history. Higher plans retain longer; a plan with unlimited retention is never cleaned up.
Warning
There is no dataRetention setting and no /organizations/{orgId}/settings endpoint. Earlier
revisions of this page showed a PATCH with dataRetention.events.retentionDays,
deletePayloadAfterDays and auditLogs.retentionDays. None of those fields exist, and the
endpoint returns 404 — so nothing was ever applied. There is also no separate payload window:
payloads are deleted on the same cutoff as the event row that owns them, so you cannot purge a
payload while keeping its metadata.
To see the window that applies to you, read your plan's limits:
curl https://api.hookbase.app/api/organizations/{orgId}/billing \
-H "Authorization: Bearer YOUR_API_KEY"Tip
Compliance
Because the window is fixed by plan, GDPR and CCPA data-minimisation has to be handled on the way in rather than on the way out. Keep PII out of the stored payload in the first place — mask the fields you do not need (see Field Masking above) and strip the rest in a transform before delivery. The API has no endpoint for deleting an individual event, so there is no way to purge a stored payload ahead of the plan cutoff yourself — for a customer erasure request, contact support.
Reliability Configuration
Configure retry policies, circuit breakers, and failover for production-grade reliability.
Retry Policies
Route deliveries use a fixed retry schedule. There is no per-destination retry configuration — a
destination takes no retryPolicy, maxAttempts, or retryableStatusCodes, and sending them is
silently ignored.
| Attempt | Sent after |
|---|---|
| 1 | immediately |
| 2 | 10 seconds |
| 3 | 30 seconds |
| 4 | 2 minutes |
| 5 | 10 minutes |
After the fifth attempt the delivery is marked failed and moves to the
dead letter queue.
What is and is not retried:
5xx,429, network errors, and timeouts are retried.- Any other
4xxis a permanent failure — the delivery stops immediately withPermanent failure: HTTP <status>rather than using its remaining attempts. A destination that answers401while a token is being rotated will not be retried, so fix the credential and replay the delivery.
A destination's timeoutMs (1000-60000, default 30000) decides when an attempt counts as a timeout.
That is the one delivery-timing knob a destination has.
Tip
Per-endpoint retry schedules exist on the sending side. If you are fanning webhooks out to your
own customers' endpoints, POST /api/webhook-endpoints takes backoffType and retryDelays per
endpoint. See the Send API reference. The schedule above is for routes.
Circuit Breaker Tuning
The circuit breaker is configured per route, not per destination — there is no
/destinations/{id}/circuit-breaker endpoint. Three settings, all changed through one endpoint:
curl -X PATCH https://api.hookbase.app/api/organizations/{orgId}/routes/{routeId}/circuit-config \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"circuitFailureThreshold": 10,
"circuitProbeSuccessThreshold": 3,
"circuitCooldownSeconds": 60
}'Configuration Explained:
circuitFailureThreshold: 10(1-50) — open the circuit after 10 consecutive failures. There is no rolling window: one success resets the countcircuitProbeSuccessThreshold: 3(1-10) — close the circuit after 3 consecutive successful probescircuitCooldownSeconds: 60(30-3600) — wait 60s in the open state before probing
There is no enable flag. The breaker is always active for a route on a plan that includes it, and
circuitFailureThreshold: 50 is the way to make it effectively never trip.
For High-Traffic Routes:
{
"circuitFailureThreshold": 50,
"circuitProbeSuccessThreshold": 10,
"circuitCooldownSeconds": 300
}For Critical Low-Traffic Routes:
{
"circuitFailureThreshold": 3,
"circuitProbeSuccessThreshold": 1,
"circuitCooldownSeconds": 30
}Read the live state with GET /api/routes/{routeId}/circuit-status, and clear a stuck one with
POST /api/routes/{routeId}/reset-circuit. See the
Routes API.
Warning
An open circuit does not activate failover. It dead-letters the delivery with
circuit_breaker_open. Keep circuitFailureThreshold above failoverAfterAttempts if you want
failover to get its turn first — see the Failover Guide.
Failover Setup
Failover is configured with two flat fields on the route. There is no failover object and no
trigger list — the only trigger is the primary destination exhausting failoverAfterAttempts.
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/routes \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Payment Processing with Failover",
"sourceId": "11111111-1111-4111-8111-111111111111",
"destinationId": "22222222-2222-4222-8222-222222220028",
"failoverDestinationIds": ["22222222-2222-4222-8222-22222222002a", "22222222-2222-4222-8222-222222220033"],
"failoverAfterAttempts": 3
}'failoverDestinationIdstakes up to 3 backups, tried one at a time in order. It may not contain the primary destination or a duplicate.failoverAfterAttemptsis 1-5 and must not exceed the 5 attempts a delivery gets.
Warning
Failover does not guarantee exactly-once delivery. A destination may receive an event that a slower primary also eventually accepts, so every failover destination should be idempotent. Nothing in the request marks a delivery as a failover attempt — add a distinguishing header to the failover destination if the receiver needs to know. See the Failover Guide.
Warning
Failover is a paid-plan feature. A non-empty failoverDestinationIds on a plan without it
returns 403 FEATURE_DISABLED.
Monitoring & Alerting
Set up proactive monitoring to catch issues before they impact customers.
Notification Channels
A channel is a destination for alerts. Creating one is two steps: create the channel, then
link it to each route you want alerts about. The create body is validated strictly and
accepts only name, type and config — any other key is a 400.
Slack — config.webhookUrl must begin with https://hooks.slack.com/:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/notification-channels \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Engineering Alerts",
"type": "slack",
"config": {
"webhookUrl": "https://hooks.slack.com/services/T00/B00/xxx"
}
}'Email — the key is emails, a non-empty array:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/notification-channels \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "On-Call Email",
"type": "email",
"config": {
"emails": ["oncall@example.com", "devops@example.com"]
}
}'Webhook (for Datadog, a custom receiver, anything HTTP) — url, plus optional
method (POST or PUT, default POST), headers and secret. A secret makes
Hookbase sign the body and send it as X-Hookbase-Signature: sha256=…:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/notification-channels \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Datadog Events",
"type": "webhook",
"config": {
"url": "https://http-intake.logs.datadoghq.com/api/v2/logs",
"headers": {
"DD-API-KEY": "YOUR_DATADOG_API_KEY"
},
"secret": "a-shared-secret-for-hmac"
}
}'The six types and the config each one requires:
type | Required config | Optional config |
|---|---|---|
slack | webhookUrl (must start https://hooks.slack.com/) | channel |
email | emails (non-empty array of addresses) | — |
webhook | url (must start http) | method (POST/PUT), headers, secret |
teams | webhookUrl (must contain .webhook.office.com/) | — |
pagerduty | routingKey (exactly 32 characters) | severity (critical/error/warning/info) |
discord | webhookUrl (must start https://discord.com/api/webhooks/) | — |
Anything else in config is stored but never read.
Linking a Channel to a Route
A channel on its own alerts about nothing. The link both scopes the alerting and carries the triggers:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/notification-channels/{channelId}/routes \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"routeId": "{routeId}",
"notifyOnFailure": true,
"notifyOnCircuitOpen": true,
"notifyOnRecovery": true,
"notifyOnSuccess": false
}'Returns {"success": true}. Linking the same route twice returns a 400
(Route is already linked to this channel); to change the triggers on an existing link,
PATCH .../notification-channels/{channelId}/routes/{routeId} instead.
What to Alert On
There are exactly four triggers, all booleans on the route link:
| Trigger | Default | Fires when |
|---|---|---|
notifyOnFailure | true | The route's consecutive-failure count reaches its failureThreshold (default 3) |
notifyOnCircuitOpen | true | The route's circuit breaker opens and deliveries start dead-lettering |
notifyOnRecovery | true | The route succeeds again after having been at or past its failureThreshold |
notifyOnSuccess | false | Every successful delivery — noisy; leave it off outside debugging |
Failure alerts fire on the crossing, not on every failure: the check is for the consecutive
count landing exactly on failureThreshold, so a route that keeps failing past it alerts once,
not repeatedly. Recovery is the mirror image — a success only alerts if the count had reached
the threshold first, so a single blip that self-heals is silent on both sides.
Warning
There is no event-name subscription model. Alerting is not configured by listing event
names such as route.circuit_opened or source.quota_exceeded on the channel — those
strings are not part of the API. The four booleans above are the complete set of triggers,
and they live on the route link, not on the channel.
Info
Notification channels require a paid plan. On the free plan every endpoint in this
section returns 403 with Notification channels are only available on paid plans.
Quota and signature-verification problems surface elsewhere: quota usage on
GET /api/organizations/{orgId}/billing (which returns plan, limits and usage) and the
dashboard, and rejected signatures in the
event list with a failed ingest status. Cron failures are alerted separately, via
notifyOnFailure and notifyEmails on the cron job itself.
CLI Monitoring Commands
Use the CLI for real-time monitoring:
# Stream live events
hookbase events follow --source 11111111-1111-4111-8111-111111111111
# Recent events for one source
hookbase events list --source 11111111-1111-4111-8111-111111111111 --limit 50
# View recent failures
hookbase deliveries list --status failed --limit 50
# Failures for one destination
hookbase deliveries list --destination 22222222-2222-4222-8222-222222220002 --status failed
# Send a test payload to a destination
hookbase destinations test 22222222-2222-4222-8222-222222220002
# Cron jobs status overview
hookbase cron status
# Watch cron executions as they run
hookbase cron follow --job cron_abc123Health Endpoint Monitoring
Monitor Hookbase's health endpoint with external uptime services:
curl https://api.hookbase.app/healthResponse:
{
"status": "healthy",
"timestamp": "2026-02-11T10:30:00Z",
"services": {
"database": "healthy",
"queue": "healthy",
"storage": "healthy"
}
}Use services like Pingdom, UptimeRobot, or Better Uptime to monitor this endpoint and alert on outages.
Access Control
Implement role-based access control and least privilege principles.
RBAC Overview
Hookbase supports three organization roles:
| Role | Permissions | Use Case |
|---|---|---|
owner | Everything an admin can do, plus changing other members' roles. Belongs to whoever created the organization and cannot be invited or reassigned through the API | Founder, account holder |
admin | Full access: invite and remove members, billing, API keys, all resources | CTO, DevOps lead |
member | Create and update sources, destinations, routes, transforms, filters | Engineering team |
viewer | Read-only access to events, deliveries, analytics | Support, monitoring |
Warning
There is no developer role, and no POST .../members endpoint. Members are added by
invitation, and the only roles accepted are admin, member and viewer — anything else is a 400
(Invalid role). An earlier revision of this page documented a developer role posted to
/members; both were fiction.
Assign a role when inviting a member — role defaults to member if omitted:
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/invites \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "engineer@example.com",
"role": "member"
}'Changing an existing member's role is owner-only — an admin gets a 403
(Only owner can change roles):
curl -X PATCH https://api.hookbase.app/api/organizations/{orgId}/members/{userId} \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "role": "admin" }'API Key Scopes
API keys support fine-grained scopes for least privilege access:
| Scope | Permissions | Use Case |
|---|---|---|
read:events | List and retrieve events | Analytics, monitoring |
read:deliveries | List and retrieve deliveries | Debugging, monitoring |
read:sources | List and retrieve sources | Configuration audits |
write:sources | Create/update sources | Automation scripts |
write:routes | Create/update routes | Dynamic routing |
admin | Full API access | Service accounts, automation |
Example: Read-Only Monitoring Key
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/api-keys \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Datadog Monitoring",
"scopes": ["read:events", "read:deliveries"],
"expiresAt": "2027-02-11T00:00:00Z"
}'Example: Route Management Key
curl -X POST https://api.hookbase.app/api/organizations/{orgId}/api-keys \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Dynamic Routing Service",
"scopes": ["read:sources", "write:routes", "read:destinations"],
"expiresAt": "2027-02-11T00:00:00Z"
}'Principle of Least Privilege
Grant the minimum scopes required:
Danger
Anti-Pattern
{
"name": "Frontend Dashboard",
"scopes": ["admin"]
}This grants full access to a client-side application, which could leak credentials.
Tip
Best Practice
{
"name": "Frontend Dashboard",
"scopes": ["read:events", "read:deliveries", "read:sources"]
}Read-only access prevents malicious users from modifying configuration.
Audit Logging
All API key usage is logged in audit logs:
curl https://api.hookbase.app/api/organizations/{orgId}/audit-logs \
-H "Authorization: Bearer YOUR_API_KEY"Review audit logs for:
- Failed sign-ins (
action: "user.login_failed") - API key creation and revocation (
action: "api_key.created","api_key.revoked","api_key.rotated") - Source configuration changes (
action: "source.updated") - Member role changes (
action: "member.role_changed")
Filter server-side with the action parameter rather than pulling everything:
curl "https://api.hookbase.app/api/organizations/{orgId}/audit-logs?action=user.login_failed&limit=100" \
-H "Authorization: Bearer YOUR_API_KEY"action, entityType and userId are the available filters; limit defaults to 50 and
caps at 100, and offset pages through the rest.
Warning
Notification channels cannot alert on audit events. They fire on route delivery outcomes
only — the four triggers in
What to Alert On. To alert on security activity, poll
GET /api/organizations/{orgId}/audit-logs with an action filter, or export the log and
feed it to your own monitoring. There are no audit.* event names in the API.
Next Steps
- Custom Domains - Set up branded webhook URLs
- Audit Logs - Review security events
- Plans & Quotas - Understand plan limits
- Notification Channels - Configure alerts