Each of our sales reps covers several industries, with different target audiences (IT leadership, marketing, general management). I built an n8n pipeline that generates a weekly base of ready-to-edit LinkedIn posts for them, sorted by industry and target. The real difficulty wasn’t wiring an LLM to a prompt.
The problem
A post that resonates with a CIO at an insurance company has nothing to do with one aimed at the marketing leadership of a software vendor. Nobody has time to personalize at that pace on top of prospecting, so posts get skipped or end up as generic shares.
The goal: start from what we already know (each rep’s industries and targets, product documentation) and produce qualified raw material, not a final post to publish as is.
What I built
Everything runs in n8n:
- two nativeData Tables:
commercial_secteurs(~110 rows: sales rep, industry, target, company size, customer references, product arguments) andproduit_faits(~50 short facts); - anAI agent (LangChain Agent node) connected to OpenRouter, with forced structured output;
- aCode node that groups posts by sales rep and generates an
.htmlpage; - SharePoint to host the file, Teams to send the link as a private message.
flowchart TD
A["Weekly trigger\nMonday 7am"] --> B["Data Tables\nproduct facts + rep x industry x target"]
B --> D{"Loop\n1 row at a time"}
D -->|for each row| E["AI agent\nOpenRouter + structured output"]
E --> D
D -->|loop done| G["Group by sales rep\n+ .html file"]
G --> I["SharePoint upload"]
I --> J{"Switch: which rep?"}
J --> K["Teams 1:1 per rep"]
Three choices matter:
- Keep the data granular. The sales rep profile file already existed for prospect sourcing. The same industry appears several times depending on whether the target is IT or marketing. I resisted the urge to deduplicate: these are different pitches.
- A product fact base as a guardrail. I extracted around fifty verifiable facts from the documentation and injected them into the system prompt, so the model doesn’t embroider on made-up features.
- Structured output rather than free text. The agent directly returns
{ "posts": [...] }with 3 proposals (social proof, industry insight, product news). Parsing free text to extract 3 posts is fragile; forcing a JSON schema removes that whole class of bugs.
For delivery, some reps cover more than 30 industry/target combinations: a Teams message listing everything would be unreadable. The file goes to SharePoint, only the link goes to Teams, through a Switch that routes to a dedicated Teams node per rep (the same pattern as my daily task reminders).
What broke
The field that vanishes after an API call
A Switch that routes nothing
On the first real test, the Switch routed zero items to its 4 branches. The SharePoint upload node replaces each item’s content with the API response: the commercial field used for routing no longer existed.
General rule: a node that calls an external API replaces the item’s data, it doesn’t enrich it. The fix is to explicitly reference the earlier node that still carried the value.
The disguised prospecting tone
The first posts all ended with “want me to show you an example tailored to your catalog?”. Correct, but they read like a private message to a prospect, not a public post. The cause was in my prompt: it asked for an “invitation to continue the conversation in a private message” at the end of the post.
The fix in the system prompt: write in the first person like a real post (field experience, opinion), never address one specific person formally, ban sales outreach phrasing, and end either with an open question to the network or with a strong observation.
Model cost
First test on a “flagship” model: about $0.037 per call, on around a hundred rows every week. For short copywriting, that was disproportionate. A much lighter model from the same provider gives equivalent perceived quality at a cost divided several times over.
Limitations
No feedback loop on generated posts, no memory of what was already suggested (risk of repetition), and the time saved depends entirely on rep adoption, since human review remains mandatory. The pipeline currently runs for two sales reps in a test phase.
Key takeaways
The AI part wasn’t the longest. The time went into structuring the upstream data, then fixing in the prompt a bias I hadn’t anticipated: a prospecting tone in content meant to be public.
On the same forced-output principle, see also Automating a Grammar Variable with AI.