Skip to content

Overview

Webhook Ingestion

Ironflow provides two ways to receive external webhooks (Stripe, GitHub, etc.) and transform them into native Ironflow events.

How It Works

Ironflow supports two ingress paths:

Path A — Server-managed source (register via Dashboard / CLI / RPC):

Provider → POST {ironflow}/api/v1/webhooks/:provider → Verify → Deduplicate → Emit Event → Trigger Functions
  1. Verification: If a verify_header + verify_algorithm (hmac-sha256 or hmac-sha1) + verify_secret are configured on the WebhookSource, Ironflow validates the HMAC signature with subtle.ConstantTimeCompare. Hex-encoded signatures only.
  2. Deduplication: Automatic idempotency by reading the top-level id or event_id field from the JSON payload (no header support, no caller override).
  3. Event name: Built server-side as {event_prefix}.{payload.type} — e.g. an event_prefix of stripe with payload.type=payment_intent.succeeded produces the event stripe.payment_intent.succeeded. The event’s source metadata column is set to webhook.
  4. Execution: The resulting event triggers matching functions.

Path B — SDK-managed webhook (createWebhook + serve()):

Provider → POST {your-app}/.../webhooks/:provider → SDK runs verify() → SDK runs transform() → POST {ironflow}/api/v1/events → Trigger Functions

The SDK mounts its handler on any URL path that contains /webhooks/:provider (regex /\/webhooks\/([^/]+)/). Your verify() runs locally; your transform() produces { name, data, idempotencyKey }; the SDK forwards it to Ironflow’s /api/v1/events endpoint. The Quick Start below uses Path B.


Quick Start (Node.js)

1. Define the webhook source:

import { createWebhook } from "@ironflow/node";
const stripeWebhook = createWebhook({
id: "stripe",
// Verification logic (HMAC, etc.)
verify: (req) => { /* ... */ },
// Transform payload into Ironflow event
transform: (payload) => ({
name: `stripe.${payload.type}`,
data: payload.data.object,
idempotencyKey: payload.id,
}),
});

2. Register with the server:

import { serve } from "@ironflow/node";
export const POST = serve({
webhooks: [stripeWebhook],
functions: [myWorkflow],
});

3. Point your provider URL:

Any path on your app that ends with /webhooks/stripe works — the SDK matches /webhooks/:provider anywhere in the URL. For a Next.js Route Handler at app/api/webhooks/[provider]/route.ts, the URL is https://your-app.com/api/webhooks/stripe.


Registration Models

ModelSetupUse Case
DashboardDashboard → Webhooks → New SourceAdd a server-managed source via UI (provider ID, event prefix, optional verify header/algorithm/secret).
CLIironflow webhook list, ironflow webhook deliveries, ironflow webhook testList server-managed sources, browse deliveries, and POST a test payload to /api/v1/webhooks/:provider.
SDK (Code)createWebhook({ ... }) + serve({ webhooks })Custom verification or payload mapping that lives next to your application code (Path B).

Monitoring & Audit

Ironflow records every Path A delivery in the webhook_deliveries table and retains rows for 30 days (hardcoded — a background sweep runs hourly). In the Dashboard → Webhooks section, you can:

  • Browse all registered webhook sources and create/delete them from the UI.
  • Open the Deliveries view to see status (accepted, deduplicated, rejected, failed), external ID, timestamp, and linked event ID.
  • Expand a delivery row to inspect raw request headers and body.

Path B deliveries are not written to webhook_deliveries — they emit straight to /api/v1/events, so they appear in the Events stream rather than the Deliveries view.

Idempotency

On Path A, Ironflow deduplicates by the top-level id or event_id field in the payload (composite unique index on (source_id, external_id)). If the payload has neither field, no dedup occurs. On Path B, dedup is whatever idempotencyKey you return from transform().