Last-click attribution tells you content does not work, right when it is starting to. Here is the lagged, assisted content attribution model we run instead.

A silent failure is the most expensive kind
The worst n8n incident we ever cleaned up was not a crash. It was a lead-routing workflow that had been failing for nine days without a single alert. The HTTP node calling the CRM hit a 429 rate limit, the execution errored, and n8n moved on. Two hundred and thirty leads never made it into the pipeline. Nobody noticed until a salesperson asked where the inbound had gone. That incident is why every workflow we ship now carries the same three-part safety net: retry with backoff, a dead-letter queue, and an alert that pages a human.
The default behavior in n8n is to fail loud in the editor and fail silent in production. We invert that.
Retry with backoff, set per node, not per workflow
n8n lets you enable retry on a node directly. On every node that touches a network, an HTTP request, a database, a webhook to an external service, we turn on Retry On Fail and set the values deliberately. The defaults are too tame. We use 3 attempts with a 2,000 millisecond wait between tries for most APIs, and we bump that to 5 attempts with 5,000 milliseconds for services we know rate-limit aggressively.
The reason backoff matters is that most transient failures are rate limits or brief network blips. A 429 or a 503 clears in seconds. Retrying instantly hammers the same wall, so we space the attempts. Where the API returns a Retry-After header, we read it in a Code node and wait that long instead of guessing. For a node that is naturally idempotent, like a GET, this is free safety. For a node that writes, retry is dangerous without one more thing, which is why we pair every retried write with an idempotency key so a replayed request does not create a duplicate record.
The dead-letter queue catches what retry cannot
Some failures are not transient. A malformed payload, a deleted record, an expired credential. Retrying those three times just wastes three attempts and then drops the item. This is where the dead-letter pattern comes in.
We attach an Error Trigger workflow to every production flow. When any execution fails after exhausting its retries, n8n fires the Error Trigger, and our error workflow does three things in order. First, it writes the full failed payload, the workflow name, the node that failed, the error message, and a timestamp into a dead-letter table in Postgres. We use a single table, dead_letter_events, with a status column defaulting to open. Second, it tags the event with a replay key so we can re-run that exact item later without re-running the whole batch. Third, it fires the alert.
The dead-letter table is the part most teams skip, and it is the part that saves the day. When the CRM credential expired on one client's flow, 40 items dead-lettered over an afternoon. We rotated the credential, ran a small replay workflow that pulled every row where status equals open and pushed it back through the original entry point, and marked them done. Zero data lost. Without the table, those 40 items would have been gone, and we would have been reconstructing them from logs if we were lucky enough to have logs.
Why the order of operations inside the error workflow matters
We were burned once by an error workflow that alerted before it persisted. The alert fired, an engineer jumped on it, and by the time they went looking for the failed payload, the execution data had already aged out of n8n's retention window. They had a notification telling them something broke and no way to see what. Now the error workflow always writes to the dead-letter table first and alerts second. Persistence is the operation we cannot afford to lose, so it goes before anything that can fail on its own, like a Slack API call. If the alert fails, we still have the record. If the record fails, the alert is useless.
We also capture the input data on the failing node, not just the error string. n8n's Error Trigger gives you access to the workflow's execution context, and the payload that caused the failure is the single most useful thing for debugging. A message saying invalid email format tells you almost nothing. The same message attached to the exact record that had a trailing space in the email field tells you everything. So the dead-letter row stores the full input JSON, and our replay workflow reads it straight back out when it re-runs the item.
Alerting that reaches a person
An alert that lands in a channel nobody reads is the same as no alert. Our error workflow posts to a dedicated incidents Slack channel with a structured message: workflow name, failed node, error text, the count of dead-lettered items in the last hour, and a direct link to the n8n execution. For anything we classify as critical, lead routing, payment events, client-facing dispatches, we also send a second alert that actually pages, so it cuts through outside working hours.
We rate-limit the alerts themselves. A flow that fails 200 times in a minute should send one grouped alert, not 200 messages, or the noise trains everyone to ignore it. The error workflow checks how many events landed in the last five minutes and collapses them into a single summary when the count crosses ten. The detail still lives in the dead-letter table for anyone who wants the full list.
Classifying failures so you do not page on noise
Not every failure deserves the same response, and treating them identically is how teams end up ignoring their own alerts. We split failures into three classes inside the error workflow. Transient failures, the 429s and 503s and connection resets, should have already been caught by node-level retry, so if one reaches the error workflow it means retry exhausted and the upstream is genuinely down. That is worth an alert but not a page during the day. Data failures, a malformed payload or a validation error, are not going to fix themselves on retry, so they go straight to the dead-letter table with a low-urgency notification, because the fix is a code or data change, not a wait. Critical-path failures, anything touching leads, payments, or client dispatches, page immediately regardless of class, because the cost of a delayed response on those paths is measured in lost revenue.
The classification is a small Switch node reading the error message and the source workflow name against a lookup. It took an afternoon to build and it is the reason our incidents channel is still trusted. When an alert fires there, people know it is real, because the noise was filtered out upstream.
The standard, in one place
Every workflow we ship at Arthea gets the same checklist before it goes live. Network-facing nodes have Retry On Fail with backoff tuned to the API. Write nodes carry an idempotency key. An Error Trigger workflow is wired up and writes to dead_letter_events with a replay key. Alerts route to the incidents channel, page on critical paths, and collapse when they flood. A replay workflow exists so any operator can re-run open dead-letter rows without engineering help.
None of this is exotic. It is the difference between a workflow you can trust to run unattended for a month and a workflow that quietly drops your leads for nine days. We run 83 agents and a couple dozen n8n workflows in production, and this pattern is why we sleep through most nights. If you want the error-workflow template and the dead-letter schema we use, reach us at arthea.ai.




Architecture Notes
Occasional insights on infrastructure, conversion systems, retention architecture, and AI deployment, shared when they’re worth reading.




