Webhooks

A webhook represents one webhook integration — a target URL where HostWebhook delivers events, with optional additional targets, payload filters, payload transforms, and signature verification.

What is a webhook?

Each webhook has:

  • NameA friendly label shown in the dashboard
  • Target URLWhere HostWebhook POSTs events (your server)
  • Ingress URLWhere your webhook source sends events — unique per webhook
  • Ingress tokenThe token in the ingress URL — rotate it to invalidate the old URL
  • Signing secretUsed to sign every delivery — verify it on your server
  • Max retriesHow many additional attempts after a failure (plan-limited: Free 0–3, Pro 0–10, Enterprise unlimited)
  • Retry delayBase delay (seconds) for exponential backoff (default: 60)
  • Rate limitMax deliveries per minute (0 = unlimited)
  • ActivePaused webhooks queue events but pause delivery
  • Incoming signatureVerify signatures from Stripe, GitHub, or custom HMAC on inbound requests
  • Payload filtersRules to accept or discard events based on payload content
  • Additional targetsDeliver each event to extra URLs in parallel with optional payload transforms

Plan limits

PlanEvents / monthMax retriesMax retry delayBackoff cap
Free1,000360 s30 min
Pro50,000103,600 s6 hr
EnterpriseUnlimitedUnlimitedUnlimited24 hr
The backoff cap is the maximum delay between any two retries regardless of how large the exponential value grows. A retry is an additional attempt after a failure — configuring maxRetries: 3 means up to 4 total attempts (1 initial + 3 retries). Exceeding your plan's limit returns 403 Forbidden.

Rotating secrets

Both the ingress token and signing secret can be rotated at any time from the webhook detail page.

  • Rotating the ingress token invalidates the old ingress URL immediately. Update your webhook source to use the new URL.
  • Rotating the signing secret invalidates the old secret immediately. Update your server's environment variable first.
There is no grace period after rotation — the old secret/token stops working the moment you rotate.

Pausing a webhook

Pausing a webhook stops delivery. Events continue to be accepted at the ingress URL and queued — they will be delivered once you activate the webhook again. Useful for maintenance windows.

Ingress protection

HostWebhook offers multiple layers of ingress protection. You can enable any combination — when multiple are active, all must pass.

Require API Key

When enabled, every request to the ingress must include a valid API key with events:write scope in the Authorization header. The API key identifies who is sending, verifies they have permission, and confirms they belong to the same organization as the webhook.

bashSending with API key
curl -X POST https://api.hostwebhook.com/api/in/YOUR_TOKEN \
  -H "Authorization: Bearer hwk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"event": "order.created"}'

Incoming signature verification

Verifies that the payload was not tampered with in transit. Configure the type and shared secret — HostWebhook validates the signature header on every inbound request.

TypeDescription
noneNo signature verification (default)
stripeVerify Stripe-Signature header (HMAC-SHA256)
githubVerify X-Hub-Signature-256 header (HMAC-SHA256)
shopifyVerify X-Shopify-Hmac-Sha256 header (HMAC-SHA256, base64)
hostwebhookVerify X-HostWebhook-Signature header (t=timestamp,v1=hmac)
staticVerify a static secret in Authorization, X-Webhook-Secret, or custom header

API Key vs Incoming Signature

They solve different problems and can work together.

API KeyIncoming Signature
Who uses itYour clients (SDK, API)External services (Stripe, GitHub)
What it verifiesIdentity + permissions (scopes)Payload integrity (not tampered)
ScopesYes (events:write, canvas:write, etc.)No
Org ownershipYesNo
Replay protectionNo (use deduplication)Yes (timestamp in HMAC)
When both are enabled on a webhook, both must pass. If the API key is valid but the signature is invalid (or vice versa), the request is rejected. Use API key alone for your own clients, signature alone for external services, or both for maximum security.
Marketplace templates auto-configure the signature type based on the source (Stripe → stripe, GitHub → github).

Payload filters

Payload filters let you discard events that don't match specific criteria — before any delivery attempt is made. Events that fail the filter are saved with status filtered and no delivery job is created.

Use payload filters to avoid spending retries and quota on events you don't care about — for example, only processing payment_intent.succeeded events from a Stripe integration that sends dozens of event types.

How filters work

All filter rules are evaluated against the parsed JSON payload. If all rules pass, the event is queued for delivery. If any rule fails, the event is discarded. Fields support dot-notation for nested properties.

Available operators

OperatorDescriptionValue required
eqField equals value (string comparison)Yes
neqField does not equal valueYes
containsField contains value (string)Yes
gtField is greater than value (numeric)Yes
ltField is less than value (numeric)Yes
existsField is present and not nullNo
not_existsField is absent or nullNo

Example — only process succeeded payments

// Payload filters on the webhook:
[
  { "field": "type", "operator": "eq", "value": "payment_intent.succeeded" },
  { "field": "data.object.amount", "operator": "gt", "value": "0" }
]

// This Stripe payload passes:
{ "type": "payment_intent.succeeded", "data": { "object": { "amount": 5000 } } }

// This one is filtered (wrong type):
{ "type": "payment_intent.created", "data": { "object": { "amount": 5000 } } }

Manage payload filters from the Payload Filters section on the webhook detail page.


Additional targets

Additional targets let you deliver each incoming event to multiple URLs in parallel — without needing multiple ingress tokens. Each target is independent: it has its own retry budget, active toggle, optional per-target payload filters, and an optional payload transform.

The primary targetUrl on the webhook always receives the original payload. Additional targets are queued in parallel and can transform the payload before delivery. Additional target failures do not affect the event's primary lifecycle status.
Enable notifyPrimaryOnFailure on an additional target if you want your primary server to know when that target permanently failed — for example, to log that a welcome email was never sent. The notification is a best-effort POST with a type: "target.failed" payload and carries a valid X-HostWebhook-Signature if the webhook has a signing secret. See payload shape →

When to use additional targets

  • Mirror events to a staging environment and a logging service simultaneously
  • Send the same event to multiple internal microservices
  • Forward Stripe events to Discord with a formatted embed (via payload transform)
  • Send a subset of events to an analytics pipeline using per-target filters

Additional target configuration

FieldDescription
urlThe target URL to deliver to (required)
nameOptional friendly label
isActiveToggle this target without removing it
maxRetriesOverride the webhook's retry count for this target (plan-limited)
filtersPer-target payload filters — same operators as top-level filters
payloadTransformJSON template with {{path}} interpolation — transforms the payload before delivery
notifyPrimaryOnFailureWhen true: if this target exhausts all retries, HostWebhook POSTs a target.failed notification to the webhook's primary target URL

Payload transforms

Payload transforms let you reshape the event before it reaches an additional target — converting a raw Stripe payload into a Discord embed, for example. Transforms use a JSON template with {{path}} interpolation.

Interpolation syntax

SyntaxDescription
{{payload.type}}Value from the event payload using dot-notation
{{headers.stripe-signature}}Value from the original inbound headers
{{meta.eventId}}HostWebhook event metadata (eventId, receivedAt)
{{payload.amount ?? 0}}Null coalescing — use fallback if path is null/undefined
{{payload.status === 'active' ? 'Yes' : 'No'}}Ternary — compare with string/number/null
{{payload.field !== null ? payload.other : 'none'}}Ternary with path resolution in branches

Example — Stripe payment to Discord embed

{
  "embeds": [{
    "title": "💳 Payment Received",
    "color": 5763719,
    "fields": [
      {
        "name": "Amount",
        "value": "{{payload.data.object.amount_received ?? 0}} {{payload.data.object.currency}}",
        "inline": true
      },
      {
        "name": "Customer",
        "value": "{{payload.data.object.customer ?? 'Guest'}}",
        "inline": true
      }
    ],
    "timestamp": "{{meta.receivedAt}}"
  }]
}
Use the Marketplace to get pre-built templates with payload transforms already configured for common integrations (Stripe, GitHub, Slack, Discord).

API

Additional targets are managed as separate resources via the /additional-targets API. See the API reference for the full schema.


Circuit Breaker

The circuit breaker automatically stops delivering to a webhook that is consistently failing — protecting both your server and your retry budget.

FieldDescriptionDefault
circuitBreaker.enabledEnable the circuit breakerfalse
circuitBreaker.thresholdConsecutive failures to open the circuit5
circuitBreaker.cooldownSecondsHow long the circuit stays open before testing300

The circuit breaker has three states: CLOSED (normal — deliveries flow), OPEN (blocked — events are queued but not delivered), and HALF_OPEN (testing — one event is allowed through to check if the webhook has recovered).

See the Circuit Breaker deep dive for state diagrams and advanced configuration.

Priority

Each webhook has a priority field (1–5, default 3). Priority 1 is critical — events for higher-priority webhooks are dequeued and delivered before lower-priority ones.

You can also set priority per-event using the X-HW-Priority header on inbound requests. This overrides the webhook default for that specific event.

Fallback URL

Configure a fallbackUrl on a webhook. If the primary targetUrl fails all retries, HostWebhook makes one final attempt to the fallback URL before marking the event as failed. Useful for disaster recovery or backup processing servers.

Chaos testing

Enable chaos testing to simulate failures in development and staging:

FieldDescription
chaosConfig.enabledEnable chaos testing for this endpoint
chaosConfig.failureRatePercentage of deliveries that will be artificially failed (0–100)
chaosConfig.latencyMsArtificial latency added to each delivery (milliseconds)
Chaos testing is intended for non-production environments. Enabling it on a live webhook will cause real delivery failures.