Build a Zapier alternative

Build a Zapier alternative as a webhook, a transform, and an HTTP action. The 9,000 connectors and their OAuth debt are the other product. Here is the split.

  • teardown
  • vibe-coding
  • scope

You can build a Zapier alternative as one linear workflow. A webhook, a transform, an HTTP call. The catalog calls Zapier that, not a marketplace of connectors.

You pay $30–$69 a month. The verdict is “For your five automations”.

Yesterday I wrote about vibe coding your own Shots.so. That was a regular backlog post. This one is a teardown: what is public about the architecture, which half you can vibe-code, and which half you should leave alone.

Build a Zapier alternative as a webhook and an action

The Zapier page splits the work into three rows.

The first row is 3 days. Webhook trigger, transform step, HTTP action. That is a public URL, a field map, and one outbound request. You can vibe-code this.

The second row is 2 weeks and up. Linear workflows, run history, retries, secrets, replay. Still your five pipes. More screens, a queue, a redacted log.

The third row is 3 months and up. The catalog writes that as a 7,000-connector marketplace, OAuth for everything, polling triggers. Zapier’s own apps intro (updated October 6, 2026) now says 9,000+ apps. Skip that row either way.

If you tell an agent to “clone Zapier”, it aims at the third row. The useful v1 is the first one.

Here is the shape of the data. Nothing else has to exist at 3 days.

const workflow = {
  id: 'wf-new-lead',
  revision: 3,
  trigger: {
    type: 'webhook',
    path: '/hooks/new-lead',
  },
  steps: [
    { id: 'filter-paid', type: 'filter', expr: 'trigger.body.plan === "pro"' },
    { id: 'map-email', type: 'transform', from: 'trigger.body.email' },
    { id: 'post-crm', type: 'http', method: 'POST', url: 'https://crm.flaviocopes.com/leads' },
  ],
}

A workflow, a published revision, a signed hook, three step kinds. Polling can wait. OAuth can wait.

The audience line on the page is an operations team running a known set of webhook-driven automations. If that is you, five pipes are enough. If the company already lives in 40 connectors and a canvas, keep paying.

The same v1 also replaces n8n and Pipedream if what you wanted was a webhook and a destination. Make is the visual cousin at $10–$29 a month. IFTTT is the personal two-step at $2.99–$14.99.

How does Zapier work

Bryan Helmig’s Scaling Zapier to Automate Billions of Tasks is the public starting picture. A July 25, 2017 note on that post says the numbers were already a year old, and had doubled or tripled. I am summarizing that write-up, not reconstructing their database.

Python and Django handle HTTP. Celery is the workflow engine. Each step is an independent task: consume some data, call an API, return some data, stay ignorant of its place in the tree. An engine in the middle strings those tasks together and owns retries, logging, and throttling.

MySQL holds users and Zap workflows. Redis holds in-flight counts for billing and throttling. Elasticsearch holds historical activity. Partner and user code ran on AWS Lambda in that write-up.

The dated numbers: over 8 million tasks a day, over 10 million inbound webhooks, about 100 Celery workers, a small RabbitMQ cluster.

Ratnadeep Debnath’s How Zapier uses KEDA (February 3, 2022) is the later public queue picture. RabbitMQ is still “at the heart of Zap processing”. They enqueue a message for each step. Workers run on Kubernetes. The workers are Python and do a lot of blocking I/O, so CPU autoscaling missed a growing backlog. They scaled on queue length instead.

Treat “Celery plus RabbitMQ is still the 2026 runner” as an educated guess. The product rule is still one step as one unit of work. Persist the output before you enqueue the next step.

A 3-day v1 does not need that fleet. One Worker, one queue, one D1 database. The catalog’s stack is Workers, Workflows, and D1. Use that, or Rails if that is your weekday language.

WEBHOOK ARRIVES Signature, then a fast 200 DEDUPE AND ENQUEUE One event id, one run ONE MESSAGE PER STEP Worker does blocking I/O then persists the output NEXT STEP OR STOP Filter, HTTP, or halt RUN HISTORY COPY Search a redacted log later
The public path: accept the hook, enqueue each step, persist before the next one, index a copy.

How Zap triggers work

Zapier’s help article How Zap triggers work (updated May 29, 2026) names two kinds. I am summarizing that page and their platform docs, not their poller.

Polling asks the app for its latest data on a timer. Dedup compares each item’s unique id to ids the Zap has seen. A new id starts a run. The interval is a plan feature: 15 minutes on Free, 2 minutes on Professional, 1 minute on Team and Enterprise. Their docs say the majority of triggers are polling.

The platform trigger guide says the API must return an array, newest first. Zapier does not automatically fetch extra pages. Deduplication is explicit: on first enable they cache every id, later polls only fire on unseen ids, and turning the Zap off clears the list.

Instant triggers wait for a webhook. Zapier does not ask. The Zap editor shows a lightning bolt. On the platform those are REST Hooks: subscribe when the Zap turns on, unsubscribe when it turns off, POST to a bundle.targetUrl.

A vibe-coded v1 needs none of that marketplace. Copy the product rule: one public hook, one event id, one run.

The catalog’s non-goal already names polling triggers. Five known webhooks do not need a poller that hits Gmail every minute.

The mini-prompt on the page is “The webhook door with a bouncer”. Verify the signature on the exact bytes. Enforce a staleness window. Deduplicate before anything runs.

async function receiveHook(req, { secret, seen }) {
  const raw = await req.text()
  const signature = req.headers.get('x-hook-signature')
  const timestamp = Number(req.headers.get('x-hook-timestamp'))
  if (!validHmac(raw, secret, signature)) {
    return { ok: false, reason: 'bad-signature' }
  }
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) {
    return { ok: false, reason: 'stale' }
  }

  const event = JSON.parse(raw)
  if (await seen.has(event.id)) {
    return { ok: true, duplicate: true }
  }

  await seen.add(event.id)
  await enqueueRun(event)
  return { ok: true, duplicate: false }
}

Acknowledge fast. Providers retry on a slow 200. A duplicate key returns success and starts no second run. Rejection logs name the reason category, never the payload.

Replay from the failed step

Zapier’s What is replay is the product rule. Their example: a form trigger, an email, a spreadsheet row. If the email sent and the row failed, replay does not fire the trigger again and does not send the email again. It tries the spreadsheet row.

Autoreplay, on paid plans, retries an errored step up to five times with growing delays: 5 minutes, 30 minutes, 1 hour, 3 hours, 6 hours. Filter and Paths steps are never replayed. You have 60 days from the trigger event.

The 2021 Zap History architecture (October 11, Zapier Engineering Team) is why that page can search millions of runs. Each execution writes the result to S3 and emits a Kafka event. A separate indexer pulls a batch, downloads the heavy I/O from S3, and bulk-indexes Elasticsearch. The UI is Next.js. Treat “S3 plus Kafka plus Elasticsearch is still the 2026 history path” as an educated guess. The useful split is that the runner writes and history reads a copy.

A 3-day v1 can skip the search page. A 2-week v1 needs per-step input, output, duration, and status in D1. Redact secrets. Bound payload size.

The mini-prompt is “Replay from step four without re-firing step two”. After each step, persist output before advancing. Replay loads those rows and starts at the failure.

async function replayFrom(db, { runId, stepId }) {
  const run = await db.getRun(runId)
  const prior = await db.outputsBefore(runId, stepId)
  return enqueueStep({
    runId,
    stepId,
    revision: run.revision,
    inputs: prior,
    attempt: run.attempts[stepId] + 1,
  })
}

That is a queue message, not Zapier’s replay. The email that already went out lives in prior and is not called again. Give each side-effecting step a stable idempotency key per run and step, because a timeout after a successful write is the murky case.

A run pins its published revision at start. The other mini-prompt is “Editing a workflow that’s currently running”. Drafts do nothing until you publish. Disable stops new runs and lets in-flight ones finish.

The connector marketplace is the other product

Zapier’s authentication docs list five schemes: OAuth v2 (preferred), API key, session, basic, digest. They refresh OAuth v2 and session tokens when the integration enables it. One connection then powers every trigger and action for that account. A second Gmail account is a second connection.

The hard half is that OAuth zoo. App review, refresh-token bugs, a partner who rotated a scope, a polling quota, a user who revoked access at 3am.

The catalog’s skip row is that marketplace. Do not start a public connector directory. Do not add polling “so we can do Gmail later”. Do not execute arbitrary user code.

A field expression is enough glue. The mini-prompt is “The dot-path resolver that fails loudly”. trigger.body.customer.email and steps.post-crm.response.id. A missing path fails with the path named. The same inputs must resolve the same way on replay, so the resolver does not read the clock or pick a random value.

A filter that returns false ends the run as filtered, not failed. Do not page someone when a filter did what you asked.

A Zapier alternative for five automations

3-DAY V1 Signed webhook trigger Filter and transform One HTTP action SKIP THIS HALF 9,000-app marketplace OAuth for every partner Polling triggers
Ship the webhook and the HTTP call. Leave the connector directory and the poller on the skip row.

I would rebuild Zapier for a four-person ops team that currently pastes Stripe webhooks into a spreadsheet they email around. One hook, a paid-plan filter, a transform, a POST to the CRM they already run. Secrets in a separate table. Run history with redacted bodies. Replay from the failed step.

I would host it next to the product, on Workers, Workflows, and D1, because that is the stack on the card. Use Rails if that is your weekday language. Add delay and email once a real run needs them. I would not build a canvas, a connector store, or a poller.

If the rent is a scheduled fetch between three internal systems, that is Make, $10–$29 a month, four days for a scheduled fetch, field mapping, and one destination. If the rent is five personal applets, that is IFTTT. If twelve coworkers already treat Zapier’s history as the audit log, keep paying.

Airtable is often the destination. How Airtable works is that split. A form that starts the hook is a Typeform replacement.

Start with the first row

Go to Build your own Zapier. Copy the AHA prompt, or the stack you already write.

Tell the agent the first estimate only: webhook trigger, transform step, HTTP action. Three days.

If the company already lives in the connector directory, keep paying.