DEV Community

Cover image for Your client's Zap stopped running and nobody noticed. We built Automations around that failure.
SharpHaw
SharpHaw

Posted on

Your client's Zap stopped running and nobody noticed. We built Automations around that failure.

There's a thread on r/zapier that every agency person recognises on sight. One Zap per client, about thirty clients, and the Zaps have started not running. No error. The poster is doing manual data transfers for ten of the thirty. Further down someone asks the real question: "how do you catch a client's Zap that silently stops running? No error — just quiet."

I run SharpHaw, a small agency in Lisbon, and I built SharpOS, the workspace we run every client engagement in. Automations is row 9 of its replacement ledger, the row that stands in for Zapier and n8n. This post is about what it does, how it's built, and the part of that row we deliberately didn't try to replace.

TL;DR: inside a client engagement the trigger that matters is a card moving on a board, and the failure that matters is one somebody reads. We built the engine on the workspace's own event bus and made a failed run something the hub shows you, then accepted a much shorter connector list as the price.

What Zapier and n8n are for, and where they strain

Both are general-purpose integration platforms: a trigger in one app, an action in another, thousands of connectors in between. That is exactly the right shape when your events live in other people's systems. A Stripe payment, a Typeform response, a row in a sheet. If that's your problem, you already have the right tool, and nothing below argues otherwise.

The strain shows up when the thing you're automating is the engagement itself. The events that matter in our work are workspace events: a card moved to Approved, a brief page created, a form on the client's site submitted, an audit finding opened, a support conversation left unanswered. To automate those from outside you first have to export them, which means a webhook per event type and a mapping layer that drifts every time a field is renamed. And when it breaks, it breaks in a tool the client has never logged into. Both platforms can alert on a failed run if you wire that up; the question is who receives the alert and whether they read it. In practice the first person to notice is the client, weeks later, asking why the follow-up email never came.

What Automations does instead

Automations is a workflow canvas inside the org: trigger nodes, optional filter and branch nodes, action nodes. The triggers are the hub's own events (boards, customers, pages, forms, audits, studio, support), plus GitHub, Google Calendar and Cal.com events, inbound webhooks, and schedules. Actions create or update cards, customers, pages and studio frames, send email, call an HTTP endpoint, publish to Instagram, X and LinkedIn, and act through whatever the organisation has connected under Integrations. Data moves between steps with a {{variable}} templates, and a node's output is also addressable by its name, so a request named "Webhook Call" exposes the previous node's status downstream.

It's org-scoped and role-gated like everything else in the workspace: the feature flag is off by default, building and enabling is owner, manager or platform admin, and any member can read run history. A workflow runs as a named user and every action node re-checks that user's permissions at execution time, so a workflow cannot do something its author couldn't. You can click around a fixture-only version at showcase/vell/automations; the feature page with the walkthrough is at sharphaw.digital/features/automations.

One owned example: this series ships through it. When a card on our Dev.to board is approved, the board's own automation publishes the card body through the connected DEV account and writes the live URL back onto the card. Nobody pastes anything.

How it works

Three decisions carry most of the weight.

The engine listens to the hub itself, and listening is cheap. Every change in the hub — a card moved, a form submitted, a customer created — is checked against a small table of what the organisation's workflows actually subscribe to. If nothing subscribes, nothing is written. If something does, an event is recorded and one run is queued per matching workflow; each run walks the graph from the trigger, fills in the variables, records a step per node and follows the edges. Filters stop a path, branches choose one.

That subscription table earned its keep on schedules. The per-minute tick used to load every workflow with a schedule trigger and evaluate its timing in code: 386 documents and 222 KB per tick, 1,440 ticks a day, almost always to fire nothing. Copying each workflow's schedule onto its subscription row, in the same write that saves the workflow so the copy can't drift, turned the tick into a prefilter.

Every run is a record, and a failed run has read state. Each run stores its trigger payload and one step per node, with a redacted input and the raw output, so the run detail shows exactly what became the next node's variables. Runs can be replayed. A failed run is marked as read the first time a member opens it, and until then it sits in the hub home's Needs attention list, one click from the run detail. First read wins: once anyone on the team has seen it, the organisation has. That is the design answer to the r/zapier question. The Zap that quietly stops is invisible because nothing owns the failure; here the failure is a row on the same screen the client opens.

Webhooks are boring on purpose. An inbound webhook trigger gets an unguessable address and a signing secret that is hashed on the server and shown once. Only diagnostic headers are kept; authorisation headers, cookies and API keys are stripped at ingest and never become variables or run history. The node's panel shows the last real payload received and a copy-as-cURL button, which is most of what you actually need when wiring a third party in.

Agents build workflows over the same API a person uses through the canvas: create the workflow, validate it (a trigger present, known node types, valid configuration, connected edges, no cycles), then enable it. A form's answers arrive as variables named after the field labels, and a blank optional answer renders empty rather than leaving a stray placeholder in your payload.

What it doesn't do

The connector list is short. Automations acts through the providers an organisation has connected under Integrations, which today is around fifteen, against the thousands a general platform offers. If your trigger lives in a system we don't connect, or your action does, Zapier or n8n is the right tool and we'll happily point a webhook at it in both directions.

Runs and events are pruned after 30 days, so it is an operational log, not an archive. There's no marketplace of prebuilt templates; a workflow starts from the canvas or from the API. And the engine assumes the workspace is where the work happens, which is the bet the whole product makes. If your team's real board lives elsewhere, the triggers you care about won't be here.

Who should keep n8n: anyone who wants to own the engine itself, run it on their own box, and connect anything. That's a legitimate preference and it isn't the one we made.

Where it sits in the service

Automations is the surface behind SharpHaw's AI Automations work: we map a client's recurring time sinks and wire the ones that belong to the engagement here, next to the boards and forms that produce the events. It ships inside SharpOS with every subscription.

A question for the people running client automations

If you've run automations for clients, where did the silent failures actually surface: in the tool, in your inbox, or in the client's complaint? I'd like to know whether the read-state approach would have caught yours.

This is part of SharpOS, surface by surface. Part 1, the whole ledger and the row we couldn't replace: We replaced a 12-tool client stack with one workspace we built.

Top comments (0)