A few hours into wiring n8n into the writing room, I hit a wall. The agent needed the credential id for a googleSearchConsoleOAuth2Api credential the bot user had just created in n8n. The id lives in the n8n database; the n8n MCP server exposes a list credentials tool; that tool should have returned it. It returned 403 Forbidden. I checked the REST endpoint underneath: same. I tried the internal /rest/credentials endpoint: 401 Unauthorized, needs a browser session cookie. I read the OpenAPI spec. n8n's Public API has no list-credentials endpoint at all. By design. Non-admin API keys cannot enumerate credentials, because credentials hold OAuth tokens and an enumerable secret store is exactly the wrong shape.
I had two boring options. Ask the human for the id. Or pop a browser session and read it out of the UI.
Instead I had the agent try one more thing.
The trick: create a one-node throwaway workflow that references the credential by name only, no id. POST it to /api/v1/workflows. If the user has exactly one credential of that type, n8n auto-resolves the reference on save and echoes the real {id, name} back in the response. Delete the probe workflow. You now have the id you couldn't list.
It worked the first try. The id came back. The agent shipped the workflow, tagged it, activated it, smoke-tested it through the live webhook. I never typed the credential id.
That ten-minute episode is what changed for me about what an AI-assisted agency actually is.
What that moment actually says about agency work
There is a default mental model of "an agency that uses AI." Three founders, ChatGPT subscriptions, slide decks with a different font. The work product is documents. The differentiator is that the documents got drafted faster.
The real shift is not about documents. It is about who is sitting at the keyboard for the long tail of operational work.
In a normal engagement, every small operational task (wire this API, add this webhook, pull this report, schedule this run) costs a human hour. The human is expensive, the human gets bored, the human leaves when the engagement ends. The work product stops the day the invoice clears.
In an engagement where the agency runs an always-on agent, those tasks are DM-shaped. "Pull last week's GSC top 10." "Add a watchdog that pings me if any workflow fails." "Extend the Search Console workflow with a sitemap ping op." The agent ships the change, exports the workflow JSON into git, writes the doc, opens a PR, and the system keeps running after the engagement closes because the workflow is a deployable artifact, not a memory in someone's head.
The credential vignette above is a tiny example. Faced with "the platform won't let you list the thing you need," a junior contractor would email the client. A senior contractor would notice the API limit and design around it. The agent did the senior contractor's move, codified the trick in a SKILL file so future agent sessions skip the reasoning, and shipped the workflow. The total elapsed time including writing this paragraph is under an hour.
That is what an AI-assisted agency ships. Not better-looking proposals. Autonomous infrastructure that keeps running after the engagement ends.
The writing room, refreshed
If you haven't read the previous post on this stack, the short version: there is a small VPS running two Docker containers. web runs a Next.js preview. bridge runs a Python Telegram bot that spawns the Cursor Agent CLI against a bind-mounted git checkout. A whitelisted Telegram message goes in, a pull request URL comes back. The contract that prevents disasters lives in AGENTS.md at the repo root.
The post predicted the next move would be services as ephemeral peers of the agent: spin up an ffmpeg container when there's video to transcode, tear it down when there isn't. That prediction was right in spirit and wrong in detail.
The better realization is to give the agent access to a separate peer service that lives outside its compose stack, carries its own state and credentials, and gets reached through the Model Context Protocol. That peer is n8n, self-hosted on the same VPS, exposing 400-plus integrations the agent didn't have to write. Same compute, same operating premise the CPU-era piece argues for inference: own the layer, pay vendors only for what you genuinely can't run yourself. The MCP wire is what makes n8n usable from inside the agent's reasoning, not just from inside its shell.
Result: the writing room now has two containers and one peer service, and the agent can build, deploy, and call workflows on that peer from a DM.
Wiring n8n into the agent, twice
The Cursor agent reads MCP server configs from .cursor/mcp.json at the project root. That file is gitignored because it carries the n8n API key. The committed template is .cursor/mcp.example.json.
The MCP server itself is the open-source n8n-mcp package. It runs as a stdio process the editor (or the agent CLI) spawns on startup. Two modes coexist: docs mode (full schema and parameter docs for every n8n node, no credentials needed) and management mode (list, create, update, execute workflows on the live instance, requires the API key). Both modes show up to the agent as mcp_n8n_* tools alongside its existing shell, edit, and search tools.
That is the laptop side. The harder side was the dockerized bridge.
The bridge runs on the VPS, in its own container, on Python 3.13. Two problems showed up immediately. First, n8n-mcp is a Node.js package and the Python base image has no Node runtime. Second, the bridge's .cursor/mcp.json cannot be committed because it carries the API key, but the agent inside the container still needs the file at the right path before cursor-agent boots.
The fix is two small changes to docker/bridge.Dockerfile plus a tiny entrypoint:
The Dockerfile installs Node 22 and runs npm install -g n8n-mcp@<pinned> at build time. Pinning the version is deliberate. npx -y n8n-mcp at runtime would refetch on every MCP boot and require runtime internet to npmjs. Pre-baking keeps cold starts deterministic.
A small docker/bridge-entrypoint.sh runs before the bridge process. On every container start it reads N8N_API_URL, N8N_API_KEY, N8N_WEBHOOK_HEADER, N8N_WEBHOOK_SECRET from .env and writes /workspace/.cursor/mcp.json, marked "_renderedBy": "fusionsync-bridge-entrypoint". On subsequent restarts the marker tells the entrypoint to re-render from env, so changing values in .env and docker-compose restart bridge actually takes effect. If a developer's hand-written .cursor/mcp.json is already on disk (the marker isn't present), the entrypoint leaves it alone.
Verifying from the host without printing the secret:
If the SHA matches the same hash on your laptop, the bridge and the IDE are speaking to the same n8n instance with the same secrets. That is the entire wiring story.
One workflow per service, switch by operation
The obvious way to build n8n workflows for an AI agent is one verb per workflow. A gsc-get-sites workflow, a gsc-inspect-url workflow, a gsc-page-insights workflow. The workflows multiply, the IDs multiply, the agent has to remember which webhook URL belongs to which verb, and every new operation is a new file.
The shape that scales is one workflow per service, switched by operation name. The webhook contract is the same for every service: POST a JSON body with {operation: "...", ...args}. A Switch node routes to one of N branches based on body.operation. Every branch converges on a single Build Response node that wraps the result in a common envelope. A fallback branch returns a structured error listing the operations the workflow actually supports.
The hero image above is the first one of these, built by the agent for Google Search Console: Webhook in, Switch on body.operation, three branches (getSites, inspectUrl, fallback), Build Response, Respond to Webhook.
The two operations that exist today are getSites (list verified properties the bot account can read) and inspectUrl (run Google's URL Inspection on any URL inside a verified property, returning indexStatusResult, mobileUsabilityResult, last crawl time, canonical, etc). Adding a third operation, say pingSitemap, is a single Switch branch plus a single HTTP Request node plus a small update to Build Response. Nothing else changes. The webhook URL is the same. The auth header is the same. The agent's mental model is the same.
A few conventions hold across every service-router workflow we ship:
Auth: Header Auth credential named agent-webhook-secret, header name N8N_WEBHOOK_SECRET. Value lives in .cursor/mcp.json on the laptop and .env on the VPS. Rotating the secret means rotating it in three places (n8n credential, .env, .cursor/mcp.json); a SHA-only diff confirms alignment without ever printing the secret.
Tags: category tag (ops, watch, content, etc.) plus agent-managed, webhook, service-router. The tag set is the agent's substitute for folders, because n8n's Public API has no folder endpoints and tags are the only programmable organization primitive.
Response envelope:{ok, operation, count, data, generatedAt} for successes. {ok: false, operation, error, supported, hint} for the fallback branch.
Future-proofing matters because services-with-many-operations is the dominant shape of every integration. Stripe has eighty verbs. GoHighLevel has more. The pattern keeps the n8n project root flat (one workflow per service, not one per verb) and gives every future caller a stable contract.
Agentic credential discovery, written up properly
The credential vignette at the top of this post is worth a longer look, because it is the most generalizable trick we found and it changes how the agent should behave on every future MCP integration.
The constraint is real. n8n's Public API intentionally hides credential listing from non-admin keys. Most MCP servers fronting a real backend will have similar limits. The naive flow is "ask the user for the id." The agentic flow is to exhaust every API avenue, then design around the limit.
The recipe (codified now in .agents/skills/n8n-workflow-builder/SKILL.md):
Try the obvious list endpoints. Confirm the failure mode (in our case, GET /api/v1/credentials returns 403, n8n_manage_credentials list returns 403, internal /rest/credentials returns 401).
Read the OpenAPI spec to confirm there is no alternative path you missed.
Probe the resolver. Build a one-node throwaway workflow that references the credential by name only. The save endpoint will either auto-resolve (you win, it echoes back the real {id, name}) or refuse (you learn the constraint).
Clean up the probe workflow with a DELETE.
If the user has exactly one credential of the required type, the probe wins. If the user has multiple, n8n won't auto-resolve, and the agent asks the human once which to use, then stores the chosen id in the workflow's doc and in the credentials registry so future workflows don't have to ask again.
That "stores the answer for next time" is the part that compounds. The agent isn't just solving a problem; it's building institutional knowledge for the next session. After a few months, the credentials registry in n8n/IDEAS.md becomes a complete map of every credential the bot can use, with ids and statuses. New workflows skip the probe entirely. They look it up.
This is the operational definition of agentic behavior I now hold the bot to: try every API avenue, design around the limit if every avenue fails, then write down the working trick so the next session doesn't have to think. Not "ask the human first." Not "give up and report the error."
What I ship from Telegram now
A working week of small operational tasks the bot has handled, paying back the writing-room overhead in real ways:
"Top 10 GSC queries last 24h." The bot also explains GSC's 1-3 day data lag and falls back to the freshest day available.
A few things are happening in those two screenshots that I want to name:
The bot reads real data from real services. The GSC query in slide 1 is not a demo; it returns actual ranking queries from the live Search Console property. The bot also detects the 1-3 day GSC data lag, explains it in plain English, and falls back to the most recent day with data. That fallback was not pre-programmed. The agent reasoned through the limitation on the fly using the MCP's node docs.
The bot self-corrects. In slide 2, my prompt was "first publish the post." The publish script reads draft: true from the post's frontmatter regardless of CLI flags, so my one-line prompt would have created a draft. The agent noticed, flipped the frontmatter, re-ran, and reported the post live at the correct URL. That is the difference between an "execute my command literally" agent and a "do what I meant" agent.
The constitution holds. The bottom message in slide 2 is the AGENTS.md open-PR check firing. I asked the bot to commit everything and push. It refused, named the existing open PR (#144), and told me to merge or close it first. The single most valuable line in AGENTS.md is the one that says "never open a PR while another is open." This is the bot honoring it. Without that rule, the bot would have stacked three PRs that all touched the same files and created weekend merge conflicts for me.
The carousel above is two slides because two real moments are worth more than ten staged ones. I will keep adding slides as new patterns emerge.
Workflows as code
Every workflow the bot ships gets exported as JSON and committed to n8n/workflows/<category>/<slug>.json. A sibling doc lives at n8n/docs/<slug>.md. A row gets added to n8n/WORKFLOWS.md, the live mirror of what is running on the instance. The operating rules live in n8n/IDEAS.md. The setup-and-troubleshooting runbook lives in n8n/SETUP.md. The agent's playbook for shipping a new workflow lives in .agents/skills/n8n-workflow-builder/SKILL.md and gets loaded automatically when the user asks for a workflow.
The point of this layout is single source of truth in git, not in the n8n database. If the n8n instance dies tomorrow, every workflow can be re-imported from this folder. Tag ids get re-resolved via REST; credential references get re-resolved via the same agentic probe described above; the webhookId field gets re-patched with a fresh UUID (a known quirk: Public-API-created webhook workflows save with webhookId: null and return 404 until the field is patched with a uuid4 and the workflow toggled). The doc tells the next maintainer (or the next agent) exactly which knobs to turn.
This is the same "infrastructure as content" pattern the previous post described for the writing room itself. Same principle, applied one layer deeper.
What's coming next: more verbs, less typing
The obvious extensions queue up naturally now that the pattern exists.
Inside the existing google-search-console-operations workflow:
pingSitemap: POST /ping?sitemap=... to Google after every blog publish. One Switch branch.
indexnowSubmit: IndexNow for Bing and Copilot AI search. Same shape, different endpoint.
getPageInsights: pull search analytics for a specific URL (clicks, impressions, CTR, position) over a date range.
New service-router workflows on the backlog:
gohighlevel-operations: first verbs getContacts, upsertContact, addToWorkflow. The agency-side automation surface.
payload-operations: searchPosts, patchPost, publishDraft. Lets the agent revise content without shelling out through the Telegram bridge.
vercel-operations: lastDeployStatus, previewLogs. Lets the bot triage Vercel failures the moment they happen.
Standalone:
sys-watchdog: schedule every hour, list workflow executions, ping Telegram if anything failed or hasn't run in its expected window. This is "workflow zero." Every other workflow gets safer the day it ships.
watch-vendor-changelog: RSS poll for the Meta dev changelog, Cursor changelog, Anthropic, OpenAI, Google AI. Append new entries to a tracked news-watch.md file in this repo via a GitHub commit node. High-priority hits ping Telegram.
Every one of those is "one Switch branch" or "one schedule-triggered workflow with a clear contract." Each ships as its own PR, gets its own doc, gets its own row in WORKFLOWS.md. The agent's job is the building. Mine is the merging.
What this is NOT
Three things this stack deliberately is not, because each one would change the security and reliability profile in ways I haven't solved.
It is not a customer-facing surface. The webhook contract uses a single shared N8N_WEBHOOK_SECRET Header Auth. Fine for an internal bot, wrong for a product. A multi-tenant version would need OAuth, per-customer scoping, signed payloads with replay protection, and a rate limit per credential. Today's setup assumes the only callers are me and the bot.
It is not action-taking on customer surfaces yet. Every workflow live today only reads. getSites reads Search Console. inspectUrl reads Search Console. The receipts above all end with information returned to me. The first workflow that writes on a customer's behalf (a CRM upsert, an outbound message, anything irreversible) will ship as a draft and stay disabled until I explicitly flip it on, per the BYOK post's rule about owning the irreversible bits of your stack. Action-taking workflows also get an extra approval step in AGENTS.md before the agent enables them.
It is not a replacement for n8n's UI. Credentials still get created in the browser, because OAuth flows need a browser. The agent can use credentials once they exist; it cannot create them. That separation is intentional. Connecting Google or Stripe to the bot account is the kind of action that should always involve a human at a real browser.
The bottom line
The previous post in this series ended with a tease about agent-managed services. The actual realization turned out to be cleaner than the tease: rather than have the agent stand up and tear down docker peers for every job, give it MCP access to a single peer service that already speaks 400-plus integrations, and let it ship workflows on top.
n8n is the peer service, self-hosted on the same VPS as the writing room.
n8n-mcp is the wire, pinned in the bridge container and rendered into .cursor/mcp.json from env on every start.
The service-router pattern keeps the project root flat: one workflow per third-party service, switch by operation name, common envelope, structured fallback.
Agentic credential discovery removes a class of "ask the user first" failures by exhausting every API avenue before bothering a human.
The git layout (n8n/workflows/, n8n/docs/, n8n/WORKFLOWS.md, n8n/IDEAS.md, n8n/SETUP.md, plus the agent skill at .agents/skills/n8n-workflow-builder/SKILL.md) keeps every workflow re-creatable from source, with a clear playbook for the next agent session.
This is the operational definition of an AI-assisted agency I keep coming back to. Not "we use AI to write better proposals." Closer to: the agency's deliverable is infrastructure that keeps running after the engagement ends, and the agent on the other end of the Telegram bot is the contractor that builds it. If you want one of these stood up on your stack, the 7-day pilot covers the same kind of inbound automation work, and the writing-room-plus-n8n setup is something I am happy to install alongside it. Same as last time: I'll be in Telegram.
Free 7-day pilot or a free AI audit
Turn Instagram and WhatsApp inquiries into booking-ready conversations.
FusionSync is the inbound operating system for event companies. Pick the starting point that fits where you are: run a free 7-day production pilot, or start with a free audit of your Instagram, WhatsApp, and CRM flow.
Not sure which fits? Pick the audit. We can scope the pilot from there.
Option 1
Free 7-day production pilot
We install the full Instagram-to-WhatsApp inbound system on one campaign you choose. You run real traffic. You decide on day seven.
Capture, qualify, route, CRM-sync on one live campaign
4 to 7 days setup, then 7 cost-free production days
Keep the same system if it works. No rebuild.
Stop with no obligation if it does not improve handoffs.
Option 2
Free AI audit of your sales process
No build, no commitment. We map where your current inbound and sales process is leaking, then hand you the AI fix order. Useful if you are not ready for a full pilot yet.
Walk-through of your Instagram, WhatsApp, and CRM flow
Map the leak points: missed DMs, cold handoffs, late sync
Written diagnosis and AI fix order, not a sales deck