How to Auto-Post to Social Media with n8n: The Full Workflow

To auto-post to social media with n8n, build one workflow with five working parts: a trigger, an AI node that writes the caption, one HTTP Request node that sends the post to PostWharf, that node's retry switched off, and a check that reads back what each network did with the post. No community node is involved. The HTTP Request node and a PostWharf workspace key reach ten networks (Instagram, TikTok, X, LinkedIn, YouTube, Facebook, Threads, Pinterest, Bluesky and Telegram), and one post counts once against your plan however many of them it goes to. The six steps below build it, with a request body you can paste.

By · · 10 min read

Drafted with AI assistance from our own research and Search Console data, and reviewed by Rahul A before publishing. Rules and prices change; check the linked official source before you act.

PostWharf's Starter plan is $0 for seven days, then $15 a month for 200 scheduled posts with unlimited channels, the REST API, webhooks and a delivery receipt on every post. Start free for 7 days.

The workflow at a glance

Stepn8n pieceWhat it doesThe setting that matters
1. KeyHeader Auth credentialHolds the PostWharf workspace keyHeader Authorization, value Bearer wf_live_…
2. TriggerRSS Feed Trigger, Schedule Trigger or WebhookStarts one run per piece of contentKeep a stable id from the item
3. CaptionAn AI model nodeWrites text that fits each networkPut the character limits in the prompt
4. PublishHTTP RequestSends POST /v1/postsJSON body, Idempotency-Key header
5. RetryThe same node's Settings tabStops n8n from sending it twiceRetry On Fail left off
6. ReceiptWait plus HTTP Request, or a Webhook nodeReads each network's resultAct on every delivery, not on the run

Before you start

You need three things from PostWharf before n8n can post anything.

  • Connected accounts. Bluesky and Telegram connect with their own credentials; the other networks are linked by signing in to them from Channels in the PostWharf dashboard. Instagram needs a Business or Creator account linked to a Facebook Page, because personal accounts cannot be posted to programmatically by any tool. Reddit, Discord, Snapchat and WordPress are request-only for now: PostWharf cannot publish to them yet.
  • The workspace API key. It starts with wf_live_, can publish to every channel in the workspace and does not expire, so it belongs in n8n's credential store rather than in a node's text. If it leaks, rotate it from Settings → API keys.
  • The channel ids. Run GET https://api.postwharf.com/v1/channels once (an HTTP Request node executed by hand will do) and note the id of each channel you want to post to. Each row also names its platform, so the Instagram channel is the one that says "platform": "instagram".

Step 1: Store the key as a credential

In n8n, create a Header Auth credential. Set the header name to Authorization and the value to Bearer followed by your workspace key. Every PostWharf request in the workflow uses this one credential, so rotating the key later means editing it in one place.

The n8n integration page shows the same request reading the key from an environment variable, as {{$env.POSTWHARF_KEY}}. That form only works on an instance that lets expressions read environment variables; the credential works on any instance and keeps the key in n8n's credential store with your other secrets.

Step 2: Choose the trigger

Each run should carry one piece of content, which becomes one post. Three starts cover most cases:

  • RSS Feed Trigger. A new blog post, release note or podcast episode becomes a post. This is the example the PostWharf n8n page builds.
  • Schedule Trigger plus a sheet. A Google Sheets or Airtable read pulls the next row, with the text, the channels and a publish time in its columns. This suits a content calendar someone else fills in.
  • Webhook. Your CMS or your app calls n8n when something is published.

Whatever starts the run, keep one value that never changes for that item: the RSS item's link, the sheet's row id or your CMS's post id. Step 4 sends it as the idempotency key, which is what makes a re-run harmless.

Step 3: Write the caption with an AI node

Add your model node after the trigger: n8n's OpenAI node, or an LLM chain node with whichever chat model you use. Give it the item's title and summary, and put the hard limits in the prompt. X takes 280 characters; an Instagram caption takes 2,200. Ask for two fields back, caption for the longer networks and caption_x for X, so each network gets text written for it rather than text cut short.

Hard-coding the numbers is optional. GET /v1/platforms returns every network's text limit as data, so a node before the prompt can read them.

PostWharf checks the text against each network's limit before it queues anything. An over-length caption is refused at once with a 422 that names the field, instead of being accepted and then rejected by the network minutes later. In practice a caption that comes back too long fails in the run you are watching, not after the rest of the batch has gone out.

Step 4: Send the post with one HTTP Request node

This node is the whole integration. Configure it like this:

  • Method: POST
  • URL: https://api.postwharf.com/v1/posts
  • Authentication: Generic Credential Type, then Header Auth, then the credential from Step 1
  • Send Headers: on, with Idempotency-Key set to the stable id from Step 2, for example {{ $('RSS Feed Trigger').item.json.link }}
  • Send Body: on, Body Content Type JSON, Specify Body "Using JSON"

The body:

{
  "text": {{ JSON.stringify($json.caption) }},
  "channels": ["ch_your_linkedin_channel", "ch_your_instagram_channel", "ch_your_x_channel"],
  "overrides": {
    "ch_your_x_channel": { "text": {{ JSON.stringify($json.caption_x) }} }
  },
  "publish_at": "2026-11-03T09:00:00Z"
}

Three details in it are worth knowing.

  • JSON.stringify around each caption. Writing "{{$json.caption}}" inside quotes looks simpler, but the first caption with a quotation mark or a line break in it produces invalid JSON and a failed run. JSON.stringify writes the text as a properly escaped JSON string, quotes included.
  • publish_at is optional. Leave it out to publish now. Send an ISO-8601 instant with Z or an offset to schedule. A time with no zone, such as 2026-11-03T09:00, is read in the workspace's time zone, which is UTC unless you set another.
  • Images and video. Register a file you already host with POST /v1/media (a public https URL and "kind": "image" or "video"), then list the returned ids in a media array. Files publish in the order you list them.

A good answer is a 201 with the post's id, its status (scheduled when you sent a time) and one delivery per channel, each pending. That means PostWharf accepted the post, not that any network has published it yet; Step 6 is where you find out.

Rehearse before going live. Add "dry_run": true to the body and execute the node. The call runs every check a real post runs and writes nothing: it does not count against your monthly posts and does not use up the idempotency key. Remove the line once the response comes back clean.

Step 5: Turn the node's retry off

Open the HTTP Request node's Settings tab and leave Retry On Fail off. This is the one setting worth getting right. When n8n does not hear back in time and retries, PostWharf has usually already accepted the first request, so the retry arrives as a second post, not a second attempt at the first. PostWharf already retries each delivery itself, waiting as long as the network asks it to, and the receipt in Step 6 tells you whether it worked.

The idempotency key from Step 4 is the second guard. A repeat of a call with the same Idempotency-Key returns the post the first call made, with a 200 instead of a 201. PostWharf keeps each key for seven days, so re-running a failed execution by hand in that time cannot queue the same post twice.

Step 6: Check what actually landed

A green run means PostWharf took the post. Whether it reached Instagram, LinkedIn and X is a separate question with one answer per network. There are two ways to get that answer into n8n.

Poll the post. Add a Wait node (for a scheduled post, until after its publish_at), then an HTTP Request node that calls GET https://api.postwharf.com/v1/posts/{{ $json.id }} with the same credential. Each delivery in the response has its own status: pending, retrying, live, failed, cancelled, or removed when a post that went live was taken down afterwards. A failed delivery carries the error text. A live one carries a url to the post on the network, and when it has none, a url_state says why: pending means it went out and the address is not known yet, so ask again; not_reported means that network returns no address; none means nothing was published on that leg. A Switch node on the status can send failures to Slack or email and write the links of live posts back to your sheet.

Or take a webhook. Register an endpoint once with POST /v1/webhooks, sending the https production URL of an n8n Webhook node and, optionally, an events list such as ["delivery.failed", "post.partial"] (leave it out to receive every event). The response includes a signing secret starting whsec_, shown only once, so store it as you create it. From then on every delivery outcome reaches n8n as it happens: succeeded, retrying, failed, or removed after publishing. A post where one network refused while another published arrives as post.partial, not as published.

Check every webhook before acting on it. Each request carries an X-PostWharf-Event header naming the event and an X-PostWharf-Signature header shaped t=<unix time>,v1=<hex>. The v1 value is an HMAC-SHA256 of the timestamp, a dot and the raw request body, keyed with the whsec_ secret. Recompute it in n8n (the Crypto node can produce an HMAC-SHA256) and drop any request whose signature does not match or whose timestamp is more than five minutes old. A failure event carries an action field naming what to do next, such as reconnecting the account. If your endpoint was down, GET /v1/events is the same receipt feed, read back. How delivery webhooks behave in general, duplicates and missed events included, is covered in social media scheduling API webhooks.

When PostWharf refuses the request

Every error comes back as JSON with an error field naming the cause. These are the four an n8n run is likely to meet, and what each one asks of the workflow.

StatusWhat it meansWhat the workflow should do
401The key is missing or invalidCheck the credential: the value needs Bearer , a space, then the key
402A plan limit is reached; the body names the limit, the plan, what was used and what is allowedAlert a person. This is not a slow-down, so waiting and resending will not help
422The request is malformed, and the body says which field (an over-length caption, a channel that is not connected)Fix the field or the caption prompt; send the item to an alert branch
429Rate limited; the Retry-After header says how longWait that many seconds, then send the same request with the same Idempotency-Key

Turn on the HTTP Request node's option to continue on error only if a branch after it reads the status, so a refusal goes somewhere instead of passing as success. How to sort publishing errors and fix each kind without posting twice is covered in social media publishing API errors.

Posting to one network

The workflow above treats every network the same way, because to n8n each one is only a channel id in the body. What differs is each network's own rules: the text limit, the formats it takes and the account it needs. The per-network setups cover those for each one, for example posting to Instagram from n8n, which covers feed posts, reels and carousels and the Business or Creator account Instagram requires. The other nine networks are linked from the n8n integration page.

Start posting from n8n

The workflow needs a PostWharf workspace with your accounts connected and its API key. Starter costs nothing for the first seven days and $15 a month after that, for 200 scheduled posts across unlimited channels, with delivery receipts on every post. Start free for 7 days.

Common questions

Do I need a community node to post to social media from n8n?

No. One HTTP Request node and a PostWharf workspace key make up the whole integration. There is nothing to install on the n8n side and nothing to keep updated, and each network is a channel id in the request body rather than a separate module with its own app credentials.

Can n8n schedule posts instead of publishing them straight away?

Yes. Send publish_at with an ISO-8601 time and PostWharf holds the post until then. Leave it out and the post goes out now. Until it starts publishing, PATCH /v1/posts/:id can still move the time or change the text.

Why did my n8n workflow post the same thing twice?

Usually because Retry On Fail was on: n8n gave up waiting, sent the request again, and PostWharf correctly treated it as a new post. Turn the retry off and send an Idempotency-Key built from the item's own id. With both in place, a retry or a manual re-run within seven days returns the first post instead of creating another.

PostWharf is priced per workspace rather than per connected channel, and every plan carries unlimited channels. See per-brand pricing.