Skip to main content

Webhook Trigger Node

Flow node

The Webhook Trigger node lets an external system start a DataLug workflow by POSTing a JSON payload to a signed URL. It sits in the Source group in the component browser: no upstream input port, just an OUT port carrying the delivered payload as row data.

When to use

  • An external system emits events (Stripe, Shopify, GitHub, a custom sender) and you want each event to trigger a DataLug run.
  • You want an inbound webhook that authenticates with HMAC (Stripe-style header) so unsigned or replayed calls are rejected before touching the runtime.
  • You need the delivery to be persisted regardless of runtime dispatch outcome so failed dispatches are diagnosable from the account webhooks UI.

Setup

  1. Create a webhook from Account -> Webhooks (Owner/Admin only).
  2. Copy the returned secret immediately -- DataLug shows it once and never again. Store it in your sender's config.
  3. Drop a Webhook Trigger node onto your flow canvas and bind it to the webhook you just created.
  4. Point your sender at the inbound URL: https://<your-datalug-host>/api/webhooks/<webhookId>.

Signature format

Each POST must include this header:

X-DataLug-Signature: t=<unix-seconds>,v1=<hex-hmac>

v1 is HMAC_SHA256(secret, "<t>." + raw-body-bytes). The raw body bytes matter -- do not JSON-reserialize the payload before hashing. Any downstream tool that supports Stripe-style signature headers can be pointed at DataLug with minor tweaks.

The t= timestamp is compared against server time with a 5-minute tolerance by default (operator override: DLR_WEBHOOK_HMAC_TOLERANCE_SECONDS). Timestamps outside the window are rejected as replays.

Delivery states

Every inbound POST creates a WebhookDelivery row -- even failed ones, so you can see rejection reasons in the management UI.

StatusMeaning
AcceptedHMAC verified, payload parsed as JSON. Awaiting runtime dispatch.
RejectedHMAC mismatch, missing/malformed signature, replay window elapsed, or unparseable JSON body. rejectionReason records which.
DispatchedRuntime accepted the run.
FailedRuntime rejected the run.
Coming Soon

Automatic runtime dispatch of accepted webhook deliveries: after a delivery is Accepted, DataLug will forward it to the runtime and transition the delivery to Dispatched (or Failed if the runtime rejects) without a manual retry.

Response codes

CodeWhen
202 AcceptedHMAC passed, delivery recorded, deliveryId returned.
400 Bad RequestPayload was signed correctly but the body is not valid JSON.
401 UnauthorizedSignature failed to verify (bad secret, tampered body, replay).
404 Not FoundUnknown webhookId. Nothing is persisted.
410 GoneWebhook is disabled. Reactivate from the management UI.

Rotating the secret

Rotate the secret whenever a sender's credentials might be exposed. Rotation is destructive to in-flight senders: the OLD secret stops verifying immediately. Plan a short cutover.

Rate limits

Two independent limiters run BEFORE the body parser so bogus traffic can't pump the delivery table:

Env varDefaultScope
DLR_WEBHOOK_INBOUND_LIMIT_PER_WEBHOOK100Per :webhookId
DLR_WEBHOOK_INBOUND_LIMIT_PER_ACCOUNT500Per account (sum across all its webhooks)
DLR_WEBHOOK_INBOUND_LIMIT_WINDOW_MS60000Shared window

Throttled requests return 429 with standard RateLimit-* + Retry-After headers so senders can back off.

Retry from the UI

Failed deliveries can be retried from Account -> Settings -> Webhook Triggers -> Deliveries. Only Failed deliveries are eligible - Rejected deliveries would just fail again (the HMAC would still be invalid), and Dispatched deliveries have already produced a run.

Retries re-drive the delivery through the same dispatcher the fresh inbound path uses. Successful retries transition the delivery to Dispatched with the new run id; failed retries stay Failed with the updated rejection reason.

Example flow fixture

See qa/fixtures/webhook-trigger-example-flow.json for a complete Webhook Trigger -> Transform -> Load reference chain with a sampleInboundPayload block that mirrors a real Stripe checkout.session.completed event.