There is a version of this post that lists forty links to other people's template galleries and calls itself a resource. This is not that. These are six workflows we end up rebuilding for almost every agency we work with, written out with the config, the schemas and the node JSON, plus the specific thing that breaks in each one once you are running it for more than a handful of clients. Copy them. They are more useful to you than they are to us.
Six Agency Automation Workflows You Can Copy, With the JSON
Sophie Adams and Jason Lee · Aug 10, 2026 · 23 min read
- templates
- agency-ops
- workflow-automation

TL;DR
- The same twelve-step workflow, run once a day for forty clients, bills as 13,200 Zapier tasks, 14,400 Make credits, or 1,200 n8n executions. The metering model decides the bill, not the template.
- Three published API limits break a copied workflow before its logic does: Google Sheets at 60 writes a minute per user, Slack at roughly one message a second per channel, and Airtable at five requests a second per base with a 30 second lockout after a 429.
- All six workflows here share the same seven parts. The three that template galleries leave out are the client resolver, the idempotency guard and the exception queue.
- n8n's public library lists 11,190 community templates, and almost none of them assume you will run the same workflow for forty different clients from one account.
- 31 percent of the 494 agency professionals AgencyAnalytics surveyed in 2026 run more than fifty clients, which is the volume at which a single-tenant template stops being a template.
Why free template galleries stop working around client twenty
Because almost every published template is single-tenant, and an agency is not.
n8n's public library lists 11,190 workflow templates. Make and Zapier both run libraries in the thousands. The quality is genuinely fine. The problem is structural: a template is written to do a thing once, for whoever imported it, with credentials hard-wired and the destination spreadsheet named in the node. Import it and it works. Import it forty times, once per client, and you now maintain forty copies of the same logic, each drifting from the others every time somebody fixes a bug in one and forgets the rest. Which platform you are cloning into changes how much that hurts, and we compared n8n and Zapier on exactly that.
That is not a hypothetical volume. In the 2026 AgencyAnalytics benchmark report, which surveyed 494 agency professionals between February and April 2026, 18 percent of agencies said they run 51 to 100 clients and another 13 percent run more than 100. Nearly a third of the field is past the point where forty copies of a workflow is a reasonable thing to own. Meanwhile 49 percent of those agencies have ten or fewer full-time staff, so there is nobody whose job is to keep forty copies in sync.
So the useful unit is not a template. It is a workflow that takes the client as an input.
How many clients an agency is actually running
Almost a third of agencies carry more than fifty clients, which is where a per-client copy of a workflow stops being maintainable.
1 to 5 clients
6%
6 to 15
25%
16 to 25
20%
26 to 50
17%
51 to 100
18%
101 or more
13%
Source: AgencyAnalytics 2026 Marketing Agency Benchmarks (2026)
The seven parts every workflow here has
Every one of the six below is the same skeleton with different work in the middle. Learn the skeleton once and the six files stop looking like six things.
- A trigger. A form, a webhook, a schedule, a status change.
- A client resolver. The first real step is always turning whatever the trigger gave you into a
client_id, then loading that client's configuration. Nothing downstream is allowed to name a client, a folder, or a spreadsheet directly. - An idempotency guard. A key derived from the inputs, checked against a log of keys already processed. Without it, a retry sends the report twice.
- The work. The part that differs between the six.
- A verification step. A check on the output before anybody sees it, not after.
- Delivery.
- An exception queue. Anything that fails a check, or returns a 429 or a 5xx, lands in one place with enough context to be replayed.
Parts two, three and seven are the ones missing from nearly every published template, and they are the three that decide whether your copy is still running in March.

Show the data behind this diagramHide the data behind this diagram
- Trigger fires (form, webhook, schedule or status change).
- Resolve client_id and load that client's configuration.
- Idempotency guard: if the key has been seen, stop, the run already happened.
- Otherwise do the work.
- Verification check on the output.
- Pass: deliver, then log the run and the idempotency key.
- Fail: write to the exception queue.
- Any 429 or 5xx during the work also writes to the exception queue.
How to read the files below
Four conventions run through all six, so they are written down once here rather than repeated in every file.
Clients live in a registry, not in nodes. One record per client, loaded by the resolver, referenced by every step after it. Adding a client is a row, not a workflow.
{
"client_id": "acme-north",
"display_name": "Acme North",
"status": "active",
"timezone": "America/New_York",
"owner_email": "ops@youragency.example",
"drive_root": "0ABCdEfGhIjKlMnOpQ",
"crm_record_id": "cr_8812",
"slack_channel_id": "C01ABCDEF",
"connected_platforms": ["ga4", "google_ads", "meta_ads"],
"report_day": "monday",
"sla_hours": 48
}
Secrets are never in the file. Every credential is a platform credential reference. If you paste an API key into a workflow you are about to share, you have created a different problem than the one you were solving.
Every run carries an idempotency key. Derived from the inputs, not from a timestamp. For a monthly report that is client_id + period. For an intake it is the form submission id. The guard is boring and it is the single highest-value node in the file.
// n8n Code node, runs immediately after the client resolver
const key = `${$json.client_id}:${$json.period}`;
const seen = await $getWorkflowStaticData('global');
seen.processed = seen.processed || {};
if (seen.processed[key]) {
return []; // already ran, stop here
}
seen.processed[key] = new Date().toISOString();
return [{ json: { ...$json, idempotency_key: key } }];
That version keeps state in n8n's own static data, which is fine to start and wrong by about client thirty. Move processed to a real table when you notice.
Every file below is a starting point with the interesting part left in. The parts that are specific to you are marked. The parts that are specific to nobody are already written.
1. Client intake to a working workspace
This is the one to build first, because it runs at the moment a client is most alert to whether you are organized.
The form answers become a client record, a folder tree, a CRM entry, a kickoff brief and a channel, in that order, with one human step at the end. What makes it worth automating is not the time saved on any single onboarding. It is that every client after this one gets the identical folder structure, which is what makes workflow two possible at all.
{
"name": "Intake to workspace",
"nodes": [
{ "name": "Intake form", "type": "n8n-nodes-base.formTrigger", "typeVersion": 2,
"position": [0, 0],
"parameters": { "formTitle": "New client intake", "responseMode": "lastNode" } },
{ "name": "Create client record", "type": "n8n-nodes-base.airtable", "typeVersion": 2,
"position": [220, 0],
"parameters": { "operation": "upsert", "matchingColumns": ["client_id"] } },
{ "name": "Folder tree", "type": "n8n-nodes-base.googleDrive", "typeVersion": 3,
"position": [440, 0],
"parameters": { "operation": "folder", "folderId": "={{ $json.drive_root }}" } },
{ "name": "Kickoff brief", "type": "n8n-nodes-base.chainLlm", "typeVersion": 1,
"position": [660, 0],
"parameters": { "promptType": "define", "text": "={{ $json.intake_answers }}" } },
{ "name": "Brief passes checks", "type": "n8n-nodes-base.if", "typeVersion": 2,
"position": [880, 0] },
{ "name": "Post to channel", "type": "n8n-nodes-base.slack", "typeVersion": 2.2,
"position": [1100, -80] },
{ "name": "Exception queue", "type": "n8n-nodes-base.airtable", "typeVersion": 2,
"position": [1100, 120],
"parameters": { "operation": "create" } }
],
"connections": {
"Intake form": { "main": [[{ "node": "Create client record", "type": "main", "index": 0 }]] },
"Create client record":{ "main": [[{ "node": "Folder tree", "type": "main", "index": 0 }]] },
"Folder tree": { "main": [[{ "node": "Kickoff brief", "type": "main", "index": 0 }]] },
"Kickoff brief": { "main": [[{ "node": "Brief passes checks", "type": "main", "index": 0 }]] },
"Brief passes checks": { "main": [
[{ "node": "Post to channel", "type": "main", "index": 0 }],
[{ "node": "Exception queue", "type": "main", "index": 0 }]
] }
}
}
What breaks first. The folder-tree step. Drive folder creation is not atomic across a tree, so a partial failure halfway through leaves a client with three of five folders and a workflow that thinks it succeeded. Make the folder step idempotent by name lookup before create, and have it return the full expected tree so the check at the end can compare against it rather than trusting the last node's exit code.
What to change. The folder names, the CRM fields, and the brief prompt. Everything else is structure. If you want the longer version of what to automate first in a studio, we wrote that up separately in intake, briefs and review loops for a content studio.
2. The weekly reporting run, fanned out across every client
This is the workflow that pays for itself, and it is also the one that reliably explodes the first time it runs for the whole client list at once.
The shape: on a schedule, read the client registry, then for each active client pull each connected platform, normalize everything into one row shape, write it, generate the commentary, and route it for a human look before it goes out. The normalization is the part worth stealing. If every platform's numbers land in one shape, adding a seventh platform later is a mapper rather than a rewrite.
{
"metric_row": {
"client_id": "acme-north",
"period_start": "2026-08-01",
"period_end": "2026-08-07",
"source": "google_ads",
"metric": "cost",
"value": 4820.55,
"currency": "USD",
"pulled_at": "2026-08-08T06:02:11Z",
"idempotency_key": "acme-north:2026-W32:google_ads:cost"
}
}
The fan-out itself needs pacing, and pacing is the thing every gallery template omits because a single-tenant workflow never needs it.
// n8n Code node: batch the client list so the run does not stampede
const BATCH = 5; // clients processed per wave
const clients = $input.all().filter(i => i.json.status === 'active');
const waves = [];
for (let i = 0; i < clients.length; i += BATCH) {
waves.push({
json: {
wave: i / BATCH,
delay_seconds: (i / BATCH) * 12, // spread waves across the minute
clients: clients.slice(i, i + BATCH).map(c => c.json)
}
});
}
return waves;
What breaks first. Rate limits, every time, and not the ones you expect. The pull side is usually fine. The write side is what falls over, because writing forty clients' rows into one workbook is forty writes in a few seconds from a single service account.
What to change. BATCH and delay_seconds, against the numbers in the next section. The rest holds. There is a fuller treatment of the reporting and QA side in our writeup on automating a paid media agency.

Show the data behind this infographicHide the data behind this infographic
| Service | Published limit | What happens over it |
|---|---|---|
| Google Sheets API | 60 read and 60 write requests per minute per user per project; 300 per minute per project | 429 Too many requests; Google recommends truncated exponential backoff with a maximum backoff of 32 to 64 seconds |
| Slack chat.postMessage | Roughly one message per second per channel, plus a workspace-wide limit | 429 with a Retry-After header giving the seconds to wait |
| Airtable Web API | 5 requests per second per base; 50 per second per personal access token | 429, after which no request succeeds for 30 seconds |
3. Sourcing and screening, with the rubric in a file
Recruiting and staffing agencies run this one hardest, but any agency hiring for itself gets the same benefit.
A candidate arrives, gets parsed into structured fields, gets scored against the rubric for that specific role, and gets routed by band. The reason to put the rubric in a config file rather than a prompt is that a file can be diffed, reviewed and pointed at when somebody asks why a candidate was screened out. A prompt buried in a node cannot.
{
"rubric_id": "senior-paid-media-2026-08",
"role": "Senior Paid Media Manager",
"must_have": [
{ "id": "platform_depth", "test": "Hands-on management of Google Ads or Meta Ads budgets above 50k USD per month" },
{ "id": "agency_context", "test": "Has carried more than one client account concurrently" }
],
"scored": [
{ "id": "measurement", "weight": 3, "test": "Evidence of owning conversion tracking or attribution setup" },
{ "id": "writing", "weight": 2, "test": "Evidence of client-facing written communication" },
{ "id": "tooling", "weight": 1, "test": "Named specific reporting or automation tooling" }
],
"bands": [
{ "min": 12, "route": "screen_call" },
{ "min": 7, "route": "human_review" },
{ "min": 0, "route": "hold" }
],
"never_auto_reject": true,
"require_evidence_span": true
}
Two flags at the bottom are doing more work than the rest of the file. never_auto_reject means the low band routes to a queue a person clears, not to a rejection email. require_evidence_span means every score has to come back with the text it was derived from, so a reviewer checks a claim in seconds instead of rereading the CV.
What breaks first. Rubric drift. Somebody edits the scoring for one role, the bands no longer mean what they meant, and last month's shortlist is not comparable to this month's. Version the rubric in the id, as above, and store the rubric_id on every score you write.
What to change. All of it, honestly. The structure is the transferable part. More on the surrounding workflow in sourcing, screening and scheduling for a recruiting agency.
4. The review loop that does not lose track of round three
Most agencies do not have a review problem. They have a review-state problem: nobody can say without asking whether a given deliverable is with the writer, the reviewer or the client, and how many times it has been round.
So model it as a state machine and let the workflow own the transitions.
{
"states": ["drafting", "auto_check", "internal_review", "revising", "client_review", "approved"],
"initial": "drafting",
"transitions": [
{ "from": "drafting", "on": "submit", "to": "auto_check" },
{ "from": "auto_check", "on": "pass", "to": "internal_review" },
{ "from": "auto_check", "on": "fail", "to": "revising", "notify": "author" },
{ "from": "internal_review", "on": "changes", "to": "revising", "increment": "round" },
{ "from": "internal_review", "on": "approve", "to": "client_review" },
{ "from": "revising", "on": "submit", "to": "auto_check" },
{ "from": "client_review", "on": "changes", "to": "revising", "increment": "round" },
{ "from": "client_review", "on": "approve", "to": "approved" }
],
"escalate_when": { "round": 3, "to": "account_lead" },
"stale_after_hours": 48
}
escalate_when is the line that changes behavior. Round three is not a quality problem, it is a brief problem, and surfacing it to the account lead automatically is how it stops being invisible. stale_after_hours catches the other failure, which is a deliverable sitting in client_review for nine days while everyone assumes somebody else is chasing.
What breaks first. People acting outside the machine. Someone approves in a thread instead of moving the state, and the workflow's picture of reality quietly diverges from the real one. The fix is not more automation. It is making the state change the same action as the approval, so there is no way to do one without the other.
What to change. The state names, to match what your team already says out loud. Do not make people learn new words for stages they already have names for.
5. The QA gate that runs before anything reaches a client
This one is short and it catches an embarrassing amount.
Every client-facing artifact passes a registry of checks before delivery. Each check is a small function with a stable id, a severity, and a decision about whether failing it blocks the send. Keeping them in a registry rather than hard-coded in each workflow means adding a check applies it everywhere at once.
{
"checks": [
{ "id": "links_resolve", "severity": "block", "applies_to": ["report", "deck", "post"] },
{ "id": "numbers_traceable", "severity": "block", "applies_to": ["report", "deck"],
"note": "Every figure in the narrative must match a row in the metric table for the same period" },
{ "id": "client_name_match", "severity": "block", "applies_to": ["report", "deck", "post", "email"],
"note": "Catches the copy-paste from the previous client. This one earns its place on its own." },
{ "id": "pii_scan", "severity": "block", "applies_to": ["report", "deck", "post", "email"] },
{ "id": "period_current", "severity": "block", "applies_to": ["report"] },
{ "id": "tone_house_style", "severity": "warn", "applies_to": ["post", "email"] }
],
"on_block": "exception_queue",
"on_warn": "annotate_and_continue"
}
What breaks first. Nothing, which is the problem. A gate with no failures for a month gets described as noise and switched off, and then client_name_match is not there on the day it was going to matter. Log passes as well as failures so the gate can show what it caught.
What to change. Add checks, do not remove them. The two worth adding first are specific to whatever your team has actually shipped wrong before.
6. The exception queue, which is the file nobody publishes
Search any template gallery for an exception-handling workflow and you will find very little, because it is unglamorous and it does not demo well. It is also the reason the other five are still running six months later.
Every failed check, every 429, every 5xx, every timeout writes one record here. The schema matters more than the storage: the record has to carry enough to replay the work without a human reconstructing what happened.
{
"exception_id": "exc_01J8ZK",
"occurred_at": "2026-08-08T06:04:41Z",
"workflow": "weekly_reporting",
"client_id": "acme-north",
"idempotency_key": "acme-north:2026-W32:google_ads:cost",
"stage": "write_metrics",
"class": "rate_limit",
"http_status": 429,
"retry_after_seconds": 30,
"attempt": 2,
"replayable": true,
"payload_ref": "s3://runs/2026-W32/acme-north/google_ads.json",
"resolution": null
}
Four fields do the real work. idempotency_key is what makes a replay safe rather than a second send. class is what lets you count failures by kind instead of staring at a list. replayable is an honest flag for the cases where it is not, such as a form submission whose upstream data is gone. payload_ref points at the inputs, because a replay without the original inputs is a rerun and a rerun gets different numbers.
What breaks first. The queue fills up and nobody looks at it. Give it an owner and a weekly number, or it becomes a landfill with good schema design.
What to change. payload_ref storage, to wherever you already keep run artifacts. Everything else in the schema earns its place the first time you have to replay a week.
What running all six actually costs in platform fees
Here is the part that decides more than the templates do, and almost nobody works it out before committing.
Take one of these workflows at twelve steps, run once a day, for forty clients. That is 1,200 runs a month. What you get billed for depends entirely on what the platform counts as a unit.
Zapier counts a task each time it successfully completes an action step, and states plainly that triggers and polling do not consume tasks. Eleven actions after the trigger, times 1,200 runs, is 13,200 tasks a month. Zapier's Professional tier starts at 750 tasks for 19.99 dollars a month billed annually, so this workload sits several tiers up.
Make counts a credit per module action, and its pricing page is explicit that reading data from an app or a webhook counts too, while routers and error handlers are free. All twelve modules, times 1,200 runs, is 14,400 credits. The Core plan is 12 dollars a month for 10,000, so again, up a tier.
n8n counts an execution as one run of the whole workflow. Its documentation is blunt about it: the number of steps and the amount of data do not change the count. Twelve steps or fifty, 1,200 runs is 1,200 executions, which fits inside the Starter plan's 2,500 at 20 euros a month. There is also a free self-hosted community edition, which changes the arithmetic again in exchange for you owning the uptime.
None of this makes one platform correct. It makes the metering model a design input. A workflow with many small steps is expensive per-step and cheap per-run, and if you are on a per-step meter the sensible move is to collapse steps into code nodes, which is exactly the opposite of what you would do for readability.

Show the data behind this chartHide the data behind this chart
| Platform | Unit | Consumed per month by this workload | Included in the entry paid plan |
|---|---|---|---|
| Zapier | Task, per successful action step | 13,200 tasks | 750 tasks on Professional, from $19.99/month billed annually |
| Make | Credit, per module action | 14,400 credits | 10,000 credits on Core, from $12/month |
| n8n | Execution, per whole workflow run | 1,200 executions | 2,500 executions on Starter, from EUR 20/month |
Which platform each of these files lands on best
| Zapier | Make | n8n | |
|---|---|---|---|
| What one unit is | A successful action step | A module action | One run of the whole workflow |
| This workload, per month | 13,200 tasks | 14,400 credits | 1,200 executions |
| Entry paid plan | 750 tasks, from $19.99/mo annual | 10,000 credits, from $12/mo | 2,500 executions, from EUR 20/mo |
| Fastest polling on entry paid tier | 2 minutes on Professional | Scheduling is per scenario | Scheduling is per workflow |
| Self-host option | No | No | Yes, free community edition |
| Best fit among these six | Intake, where step count is low and volume is per-client | The QA gate, where routers and error handlers cost nothing | The reporting fan-out, where one run touches many steps |
What one unit is
- Zapier
- A successful action step
- Make
- A module action
- n8n
- One run of the whole workflow
This workload, per month
- Zapier
- 13,200 tasks
- Make
- 14,400 credits
- n8n
- 1,200 executions
Entry paid plan
- Zapier
- 750 tasks, from $19.99/mo annual
- Make
- 10,000 credits, from $12/mo
- n8n
- 2,500 executions, from EUR 20/mo
Fastest polling on entry paid tier
- Zapier
- 2 minutes on Professional
- Make
- Scheduling is per scenario
- n8n
- Scheduling is per workflow
Self-host option
- Zapier
- No
- Make
- No
- n8n
- Yes, free community edition
Best fit among these six
- Zapier
- Intake, where step count is low and volume is per-client
- Make
- The QA gate, where routers and error handlers cost nothing
- n8n
- The reporting fan-out, where one run touches many steps
Prices and allowances from each vendor's published pricing page, read August 2026. The consumption figures are arithmetic from their stated billing models applied to one twelve-step workflow run daily for forty clients, not measured usage.
When to stop copying and build something
Copying is the right move far longer than most vendors will tell you. The honest markers that you have outgrown it:
The registry has outgrown the tool. When per-client configuration stops fitting in a row and starts needing conditional logic per client, you are writing a program in a spreadsheet.
The exception queue has a pattern. One kind of failure making up most of the queue for a month is a design problem, and no amount of retry tuning fixes a design problem.
A step needs judgment rather than rules. Screening on stated criteria is rules. Reading a messy brief and working out what the client actually asked for is not, and that is where an agent earns its place instead of a branch.
You are paying more in platform fees than the work costs. It happens, and it happens quietly. Run the arithmetic above against your real client count before you renew.
If you get to that point and want it built rather than assembled, that is the work we do. A one-off starter build runs 1,500 to 2,500 dollars fixed, a two-week production sprint is 5,000 dollars fixed, and retainers start at 5,000 dollars a month. The whole ladder is on our pricing page, and the first two of those are deliberately cheap enough that you should not need to hire an agency automation consultant on a retainer to find out whether this works for you.
The questions we get about these files
Can I just import these and run them?+
The n8n workflow in section one imports and runs after you attach credentials and set the folder ids. The rest are config and schemas rather than executable workflows, deliberately, because the executable part of a reporting or screening workflow is mostly the connections to your specific stack. The structure is the transferable half, and it is the half that takes the longest to get right.
Do these work in Zapier or Make instead of n8n?+
Yes, with a caveat about cost rather than capability. All three platforms can express every workflow here. The difference is metering: this workload bills as 13,200 tasks on Zapier and 14,400 credits on Make, against 1,200 executions on n8n, because Zapier and Make count steps while n8n counts runs. On a per-step meter, collapse the small steps into code nodes before you import.
What is the single most important part to copy if I only take one thing?+
The idempotency guard. It is about eight lines and it is the difference between a retry being safe and a client receiving two invoices, two reports, or two rejection emails. Everything else on this page is an improvement to a workflow. That one is the thing that stops a workflow from doing damage when it fails.
How do I decide the batch size for the reporting fan-out?+
Set it against the tightest published limit in the run and leave headroom. Airtable allows five requests per second per base and then refuses everything for 30 seconds after a 429, Google Sheets allows 60 writes per minute per user per project, and Slack allows roughly one message per second per channel. Count the requests one client generates, divide the tightest ceiling by that, then halve it, because your run is not the only thing using those credentials.
Why is there no template for the exception queue in any gallery?+
Because it does not demo. A gallery template has to show a result in a screenshot, and an exception queue's best day is an empty one. It is also the piece most tied to how a specific team works, since the schema is easy and the operating habit of actually clearing the queue is the hard part.
Is it cheaper to self-host?+
In platform fees, usually yes. n8n publishes a free self-hosted community edition, and for a workload that fits comfortably on one small server the license cost goes to zero. What you take on instead is uptime, upgrades, backups, and being the person who gets paged when a scheduled run does not fire. That is a real cost, it is just not on an invoice. If nobody on your team wants that pager, the hosted tier is the cheaper option once you price the hour.
How much of this should an agency automation consultant be doing for us versus handing over?+
Handing over. If the workflows, the registry and the credentials do not end up in accounts you own, you have bought a dependency rather than an automation. Every file on this page is written to be readable by somebody who did not build it, which is the same standard to hold an outside builder to.
Copy the files. Call us if the exception queue fills up.
If you take these and they run, that is a good outcome and it cost you nothing. If you get three months in and the queue has a pattern nobody can clear, that is the point where an outside pair of hands is worth paying for.
A one-off starter build is $1,500 to $2,500 fixed. A two-week production sprint is $5,000 fixed.

Written by
Sophie Adams · Technical Writer
I turn complex AI concepts into step-by-step guides readers can follow as they work.
Journaling

Written by
Jason Lee · AI Documentation Specialist
I write AI product documentation that tells people what to do next without making the product harder than it is.
Building side projects



