Automate LinkedIn with n8n: Three Workflows That Actually Send
n8n will happily fire two hundred invitations at nine in the morning, and LinkedIn will read that as exactly one thing.
n8n’s built-in LinkedIn node has one operation, Create a post, so every outreach workflow on this page runs through the HTTP Request node against an account-based LinkedIn API. Three workflows cover most of what teams build: enrich a profile then invite, follow up when the invitation is accepted, and route replies out of LinkedIn. All three are HTTP Request and Webhook nodes with no custom code.
The part that decides whether they survive is not the wiring. It is that a scheduler has no idea what an account can afford to send, so the sending budget has to live on the server. Across 20,000+ LinkedIn accounts, gtm-api.com reports under a 1% ban rate on that arrangement.
On this page
The one credential everything runs on
The three workflows below use one credential and three node types. The last row of the table is the community-node alternative, which exists on self-hosted n8n only. In n8n, create a Header Auth credential under Generic credentials, with the name Authorization and the value Bearer followed by your API token. Every HTTP Request node in every workflow then selects Generic credentials and that one entry.
| n8n node | Job in these workflows | Key settings |
|---|---|---|
| HTTP Request | Every outbound call: enrich, invite, message | Method POST, Authentication Generic credentials, Header Auth, Send Body as JSON |
| Webhook | Receives LinkedIn events pushed to the workflow | HTTP Method POST, and the Production URL, see the trap below |
| LinkedIn (built-in) | Publishing posts, and nothing else | Post: Create, as person or organization |
| gtm-api (community node) | Invitations, messages, enrichment and people search as four operations, plus a trigger on three LinkedIn events. Self-hosted n8n only | Install n8n-nodes-gtm-api from Settings, then Community Nodes. Its own LinkedIn API credential, one API Key field, setup on n8n LinkedIn node |
The base URLs are https://app.gtm-api.com/linkedin/v4 for account actions and https://app.gtm-api.com/orchestration/v4 for webhook subscriptions. What each platform’s built-in LinkedIn app can and cannot do, including Zapier and Make, is on LinkedIn integrations.
Workflow 1: enrich a profile, then invite
This is the workflow most tutorials get wrong, and the reason is one field. The send endpoint takes profile_id, which is a LinkedIn URN in the ACoAA... form. It does not take a profile URL. So a list of profile URLs cannot go straight into a send step, and the enrichment call that resolves the URN is mandatory.
Three nodes, in order.
- Trigger. Whatever produces profile URLs: a Google Sheets row, a form submission, a CRM webhook. One item per person.
- HTTP Request, enrich. POST to
/api/linkedin-enrichment/person-full-profile. The body takespublic_identifier, which accepts the whole profile URL and reduces it to the slug itself, pluslinkedin_account_sidfor the account you are acting as. - HTTP Request, invite. POST to
/api/linkedin-connection-requests/sendwithprofile_idtaken from the previous node’s response, plus the note.
POST https://app.gtm-api.com/linkedin/v4/api/linkedin-enrichment/person-full-profile
{
"public_identifier": "{{ $json.profile_url }}",
"linkedin_account_sid": "ln_ac_..."
}
POST https://app.gtm-api.com/linkedin/v4/api/linkedin-connection-requests/send
{
"linkedin_account_sid": "ln_ac_...",
"profile_id": "{{ $json.result.profile.ln_id }}",
"note": "{{ $json.note }}"
}
The enrichment response carries one more field worth branching on. result.profile.network_distance returns the relationship degree relative to the account you pinned, so an IF node on 1st lets the workflow message existing connections and invite everyone else. Sending an invitation to somebody you are already connected to wastes a slot out of a weekly ceiling that is roughly 100, which LinkedIn connection request limits covers in full.
Workflow 2: invitation accepted, send the follow-up
Polling for acceptances is the expensive way to do this. A workflow that checks every five minutes spends the account’s read budget on questions that almost always answer “nothing new”, and it still arrives late. A webhook subscription pushes the event the moment it happens.
Set the Webhook node’s HTTP Method to POST, publish the workflow, and copy the Production URL. Then register it once, from a terminal or a one-off HTTP Request node.
POST https://app.gtm-api.com/orchestration/v4/api/webhooks
{
"target_url": "https://your-n8n-host/webhook/linkedin-accepted",
"events": ["linkedin-connection-requests.accepted"]
}
The response carries a 32-character hex secret, and it is returned once and masked on every later read, so store it when you see it. Use it to verify the HMAC on incoming calls before the workflow acts on them, because a webhook URL is a public endpoint that anything can post to.
The follow-up step is then a single HTTP Request to /api/linkedin-messages/send, with ln_id for a new thread and text for the body. Wait a realistic interval first. A message that lands in the same second as the acceptance reads as a machine to the person as much as to LinkedIn.
Workflow 3: route replies out of LinkedIn
The third workflow has nothing to do with sending. Subscribe a second Webhook node to linkedin-messages.received, and every inbound reply arrives in n8n as an event you can route: a Slack notification, a CRM note, a task, a Google Sheets row. Teams that add this one first usually find it is the workflow they actually needed, because the bottleneck in LinkedIn outreach is rarely the sending.
Two more event types earn a place in the same workflow. linkedin-account-smart-limits.limit-reached fires when an account runs out of budget for an action type, which is the signal to stop queueing. linkedin-accounts.login-failed means the session needs attention, and it is worth a direct alert, because an account that is signed out sends nothing and reports no error to your workflow.
Let the server pace it
The instinct in n8n is to control timing in the workflow: a Wait node between items, a Schedule Trigger at a safe hour, a batch size. That instinct produces a fixed cadence, and a fixed cadence is a signature. It also cannot know what the account can afford today.
The budgets that matter sit on the server, per account and per action type, and a workflow inherits them without configuring anything.
- Per-action daily caps, scaled by warm-up. Connection requests default to 25 a day and messages to 50, and both are scaled by a warm-up score from 0 to 100 computed from the account’s age, history and session spread. A new account operates at a fraction of that.
- Randomised spacing. Invitations are spaced around 10 minutes apart and messages around 8, each gap drawn independently, because a fixed cadence is itself a signature.
- Only successes count. The counter increments when LinkedIn confirms the action, so a failure costs no budget.
- Real recovery windows. When LinkedIn refuses, the account pauses on that action for the real recovery period.
So the correct n8n design is to push the whole list and let the server hold it back, and to subscribe to the limit event if the workflow needs to know. Every per-action number and how capacity is earned is on LinkedIn limits, and the multi-account arithmetic is on Scaling LinkedIn outreach.
What breaks these workflows in production
Five failure modes account for most of the time lost on these builds, and four of them produce no error message in n8n.
- The wrong webhook URL. n8n registers a test webhook only while you are listening for a test event, and the production one only when the workflow is published. A subscription pointed at the test URL works once during setup and silently never fires again.
- An n8n host the internet cannot reach. The subscription requires an https target on a public address, so a local install needs a tunnel or a cloud instance before any event arrives.
- Registering the same URL twice. One target URL can hold one subscription per team, so a second registration on the same URL is refused, and the first subscription keeps whatever event list it was created with. Put every event type you want in the one
eventsarray. - An invitation note over the cap. The note is capped at 200 characters for a sender without premium and 300 with it, and an over-length note is refused rather than truncated. Trim before sending, or set the fallback flag deliberately.
- Re-sending to somebody already pending. A second invitation while one is outstanding is refused as a duplicate. Filter your list against what is already in flight.
Frequently Asked Questions
How do I send a LinkedIn connection request from n8n?
Through the HTTP Request node, calling an account-based LinkedIn API. n8n’s built-in LinkedIn node only creates posts, and LinkedIn’s own API does not expose invitation sending outside a closed partner program. The one detail that catches people is that the send endpoint takes a LinkedIn URN rather than a profile URL, so an enrichment call has to resolve the URN first.
Which n8n node do I use for LinkedIn outreach?
On n8n Cloud, HTTP Request for every outbound action and Webhook for inbound events, and no custom code node is needed for any of the three workflows on this page. On self-hosted n8n there is now a community node, n8n-nodes-gtm-api, with the invitation, message, enrichment and people-search calls as four operations plus a trigger on three LinkedIn events. Install, credential and operations are on the n8n LinkedIn node page. The built-in LinkedIn node stays useful for publishing posts and can sit in the same workflow on its own credential.
Why does my n8n webhook never fire?
Almost always one of three things. The subscription points at the Test URL, which only exists while n8n is listening for a test event. The Production URL is the one that appears when the workflow is published. Or the n8n host is not reachable from the internet, so the delivery cannot land. Or the workflow was never published after the webhook node was added.
How do I stop an n8n workflow from overrunning LinkedIn’s limits?
By not pacing it in n8n. Wait nodes and schedule triggers produce a fixed cadence, which is itself a detectable pattern, and they cannot know the account’s current ceiling. An account-based API enforces per-action daily budgets and randomised spacing on the server, so the workflow can push the whole queue and subscribe to the limit-reached event if it needs to react.
Do these workflows need self-hosted n8n, or does Cloud work?
Both work for the sending side, since those are ordinary outbound HTTP calls. The webhook workflows need an address the internet can reach, which n8n Cloud provides by default and a local install does not. A self-hosted instance behind a firewall needs a tunnel or a public hostname before any event is delivered.
Sources & Further Reading
- n8n, LinkedIn node reference (the single Post: Create operation), read 20 August 2026
- n8n, HTTP Request node (generic credentials, Header Auth, Send Body as JSON), read 20 August 2026
- n8n, Webhook node (the Test URL is registered on Listen for Test Event, the Production URL when the workflow is published), read 20 August 2026
- gtm-api.com, developer documentation (the endpoints, request fields and webhook event names used above)
- Per-action defaults, warm-up scaling and recovery windows are self-reported from the engine running 20,000+ connected accounts
- Related: LinkedIn integrations · LinkedIn limits · LinkedIn connection request limits · LinkedIn API: the developer guide · Scaling LinkedIn outreach · n8n LinkedIn node · Zapier LinkedIn integration
Wire it once, and let the server hold the queue back.
One credential, ordinary HTTP nodes, and per-account budgets enforced before anything reaches LinkedIn. On that stack, gtm-api.com reports 20,000+ accounts at under a 1% ban rate. Free plan, then from $10 per account at volume.
Last updated: September 2026 · n8n node behaviour re-read on n8n’s own documentation, and every request field checked against the published API contract, on 20 August 2026. The community-node row and FAQ added 2 September 2026, read from the package on the npm registry
