Integration engine · running live

Webhooks that survive
the real world.

Anyone can call an API once. The hard part is the Tuesday the destination is down, the same order arrives twice, and someone has to prove what happened. Conduit takes an event on a signed endpoint, runs it through steps you can watch, retries what fails, and keeps the receipt.

  • Signed endpoints
  • Idempotency keys
  • Backoff & dead letters
  • Replay any run

Loading a flow…

 

POST
Secret

Click a step to open it · the canvas follows the event as it runs

No runs yet.

Deliveries land here. The built-in inbox is a real destination — it stores exactly what was sent, with the signature header.

This endpoint is public. Point Stripe, Shopify, a form or your own script at it, or paste this into a terminal.

The signature header is t=<unix seconds>,v1=<HMAC-SHA256 of "t.body"> — the same shape Stripe uses. Turn the switch off above if you would rather send unsigned events while you experiment.

The whole flow is data. This is what the engine stores and runs.

How it works

Four moving parts, and nothing hidden.

  1. 01

    An event arrives

    Your endpoint checks the HMAC signature against the flow's secret, with a five-minute tolerance, then looks at the idempotency key. A repeat inside 24 hours returns the original run instead of doing the work twice.

  2. 02

    The pipeline runs

    Filter, reshape, branch, ask a small model to label it, call another API, wait, deliver. Every step records what it received, what it produced and how long it took.

  3. 03

    Failures come back

    A destination that answers 5xx, times out or refuses the connection is retried after 5, 12 and 40 seconds. Anything still failing is parked as a dead letter with the whole trace attached.

  4. 04

    You can prove it

    Open any run and read it end to end: the raw event, each step, the signed body that went out, the status that came back. Replay it against the current version of the flow with one click.

Reliability

The parts nobody demos, because they are not pretty.

These are the reasons integrations fall over in month three. They are built in here, not planned for later.

Signatures both ways

Incoming events are verified with HMAC-SHA256 over timestamp.body, compared in constant time, rejected outside a five-minute window. Outgoing deliveries are signed the same way, so the receiving end can check us too.

Idempotency

Senders retry. They always do. A repeated Idempotency-Key inside 24 hours returns the first run's result and does no work, so a customer is not charged, emailed or created twice.

Retries with backoff

Only the retryable failures — 5xx, 429, timeouts, connection errors — earn a retry. A 400 does not; retrying a malformed request forever is how queues die. Three attempts, then the dead-letter list.

Resume, not restart

A retry picks up at the step that failed with the payload it had, so the steps before it never run twice. That matters when step two already charged a card.

Nothing calls inwards

Steps that reach out are https only, and private, loopback and link-local addresses are refused before the request is made. A pipeline that fetches URLs is an SSRF hole unless somebody closes it.

Bounded everything

Body size, step count, outbound calls per run, model calls per run and per day, events per flow. Every limit answers with a plain sentence rather than a stack trace.

The same work, once

A label is a pure function of the model, the label list and the text, so it is stored and reused: an event that has been seen before comes back in milliseconds and costs nothing. It is also the honest answer to the slowest thing here — a fresh model call on the free tier can take a few seconds, and nothing in this pipeline should pay that twice for the same input.

Built

What it is made of.

One Worker holds the whole engine: the public endpoint, the runner, the retry scheduler and the API this page talks to. Flows, runs, traces and deliveries live in D1. The labelling step calls a small open-weight model on Workers AI — a couple of neurons per event, no external AI account.

Retries are picked up two ways: anything touching a flow drains the ones that are due, and a cron trigger every minute catches the rest. That is why a retry lands while you are still looking at the run.

A label is a pure function of the model, your label list and the text, so it is cached: the same event never pays for a second model call. That is also why the samples on this page come back in a few hundred milliseconds while a brand-new event waits on the model.

Runtime
Cloudflare Workers, no container, no server to patch
Store
D1 — flows, runs, traces, deliveries, daily counters
Model
Llama 3.1 8B on Workers AI, for the labelling step only
Front end
Hand-written HTML, CSS and JavaScript. No framework, no build step, no libraries

Measured on this deployment

  • ≈ 170 ms end to end for a run that reshapes an event and delivers it
  • ≈ 330 ms for a run whose label is already in the cache
  • 2–10 s when the model has to label something it has not seen — on the free tier that latency is the part I do not control
  • 2–3 neurons per fresh label, on the Workers AI free allowance
  • 5 · 12 · 40 s the wait before attempts two, three and four

For a client I would add: a queue in front of the runner, so a slow model call or a burst never holds the sender's connection, per-tenant keys and role-based access, a dead-letter view with bulk replay, alerting on the failure rate, and retention that matches whatever their auditors ask for.

Questions

Fair questions.

Is this a real product I can use?

It is a working demo, and it is honest about that. Flows you build live for seven days and are capped at a few hundred events. If you want this shape of thing in your business, the useful version is one built around your systems — that is the work I do.

Can I point a real webhook at it?

Yes. Create a flow, copy the endpoint, and paste it into Stripe, Shopify, Typeform, GitHub or anything else that posts JSON. Turn the signature switch off if the sender signs with its own scheme, or leave it on and sign with the secret shown here.

What does the model actually decide?

Only the labelling step, and only from a fixed list of labels you write. It never invents a field, and if its answer is not one of your labels the step is marked unsure rather than guessing quietly. Everything else — filters, branches, mapping — is plain, deterministic code.

Where does my data go?

Into D1, on Cloudflare, in the flow you created, and out again to whatever destination you chose. Flows expire after seven days and take their runs and deliveries with them. Do not send anything you would not paste into a public demo.

Why build it on Workers?

Because the whole thing — endpoint, engine, scheduler, database and the small model — fits in one deployment with no server to run, and it costs nothing at this size. For a client with real volume I would make the same call or a different one depending on their stack; I would rather say that than pretend one answer fits everything.

Got an integration that keeps falling over?

Webhooks, syncs, legacy APIs and the plumbing between systems that were never meant to talk — that is most of what I do. Tell me what breaks and I will tell you what it takes.