Skip to content
SocialsForAI
Menu

Post to social media from n8n

Post to Bluesky, X and LinkedIn from an n8n workflow.

One HTTP Request node, one JSON body. Every network, a per-account receipt, and no double posts when a workflow retries.

The short answer · Updated Nov 1, 2026

To post to social media from n8n, add an HTTP Request node that sends POST https://socialsforai.com/v1/posts with your API key in an Authorization: Bearer header and a JSON body listing account ids and text. Add "wait": true to get each account's result in the response, and send an Idempotency-Key built from the source item's id so a retried execution never posts twice.

What n8n is

n8n's docs describe it as "a fair-code licensed workflow automation tool that combines AI features with business process automation." Workflows are chains of nodes, and the HTTP Request node calls any REST API.

For posting, n8n is the right tool when something else decides what to post: a new row in a sheet, an RSS item, a form submission, a release webhook. We use the social media API over REST here rather than MCP. n8n's MCP Client Tool node documents its connection field as an SSE endpoint, and our server speaks Streamable HTTP, so the HTTP Request node is the reliable route.

HTTP Request node settings

These settings use the HTTP Request node's own labels from n8n's docs. For authentication, either turn on Send Headers and add the header by hand, or use a Generic credential of type Header Auth so the key isn't stored in the workflow.

With wait: true the response is the post itself, with one target per account (status, url, error). Without it, the request returns as soon as the post is queued and you poll GET /v1/posts/:id or use webhooks. Turn on Never Error under Response if you want to branch on 422 responses instead of failing the node.

text
HTTP Request node
  Method:              POST
  URL:                 https://socialsforai.com/v1/posts
  Authentication:      Generic credential type → Header Auth
                       (Name: Authorization, Value: Bearer sk_...)
  Send Headers:        on
    Idempotency-Key    = {{ $json.id }}-bluesky
  Send Body:           on
  Body Content Type:   JSON
  Specify Body:        Using JSON
  {
    "accounts": ["acc_..."],
    "text": "{{ $json.text }}",
    "wait": true,
    "metadata": { "source": "n8n", "row": "{{ $json.id }}" }
  }

Idempotency keys that survive retries

n8n's error-handling docs show a retried execution as a separate execution with its own id and a retryOf field pointing at the original. $execution.id is unique per execution, so using it as the key protects you within one execution but not across a retry. Build the key from the source item instead, such as a sheet row id, an RSS guid or an order number. Same key and same body returns the original post with Idempotent-Replayed: true; same key and a different body returns 409 idempotency_key_reused. The docs cover every field.

You can paste this curl into the node's Import cURL option to fill the fields. n8n's docs note imported numbers and booleans arrive as strings, so switch Specify Body to Using JSON afterward to keep wait a boolean.

bash
curl https://socialsforai.com/v1/posts \
  -H "Authorization: Bearer sk_..." \
  -H "Idempotency-Key: row-42-bluesky" \
  -H "Content-Type: application/json" \
  -d '{"accounts":["acc_..."],"text":"New post","wait":true}'

Reading the response in the next node

With wait: true, the node's output item is the post. Branch on its top-level status: published when every account succeeded, partially_published when some did, failed when none did. For detail, split targets into items and route each by its own status; a failed target carries error.code, error.fix and error.retryable. Retryable failures are already being retried on our side (after 30 seconds, 2, 10 and 30 minutes, then 2 hours), so a workflow should not resend them.

The metadata object you sent comes back on the post unchanged, which is how a later node or a webhook handler finds the sheet row or RSS item the post came from. With Never Error on, a 422 arrives as a normal item whose error.issues lists every check that failed.

text
// HTTP Request node output with "wait": true
{ "id": "post_Zt6Mw1Qe8Rn4Yb2C", "object": "post", "status": "partially_published",
  "metadata": { "source": "n8n", "row": "42" },
  "targets": [
    { "account_id": "acc_Bs2rTq8Lm5Wn1KdP", "platform": "bluesky", "status": "published",
      "url": "https://bsky.app/profile/you.bsky.social/post/<rkey>", "error": null, ... },
    { "account_id": "acc_Li4nKd8Qp2Zr7TmX", "platform": "linkedin", "status": "failed",
      "error": { "code": "account_needs_reauth", "fix": "Reconnect the LinkedIn account with a new connect link.", "retryable": false }, ... } ] }

// A 422 when preflight fails (Never Error on)
{ "error": { "code": "preflight_failed", "message": "The post would fail on 1 check(s). Nothing was published.",
             "issues": [ { "code": "text_too_long", "field": "platform_options.bluesky.text", "fix": "..." } ] } }

Three workflows to start with

n8n runs workflows rather than chat prompts, so here are three common shapes and the API calls each makes.

  • Sheet row to scheduled post: a trigger on new spreadsheet rows, then the HTTP Request node with text from the row, scheduled_at from a date column, and Idempotency-Key set to the row id. We hold the post until it's due; DELETE /v1/posts/:id cancels it until it starts publishing. More in schedule LinkedIn posts.
  • RSS to Bluesky and LinkedIn: an RSS trigger, then one request with both account ids, the full text for LinkedIn and platform_options.bluesky.text for the 300-grapheme version. The key is the item's guid.
  • Failure alerts: register a webhook with POST /v1/webhooks pointing at a webhook trigger in your workflow, and branch on post.failed and account.needs_reauth events to message yourself. Verify the Socials-Signature header (HMAC-SHA256 of <t>.<raw body>) before trusting the payload. See webhooks.

An approval step in the workflow

For approval in n8n, put a human step between the draft and the publish: send the dry-run response to yourself (email, Slack, a form) and continue the workflow only on approval. n8n's Wait node can pause an execution until a resume URL is called. Keep the same Idempotency-Key for the dry run's follow-up publish; a dry run doesn't use the key up. The account caps and duplicate check described under keeping a human in the loop apply to REST calls exactly as they do to agents.

n8n-specific fixes

These come from how n8n builds requests. The API's own codes are the same over REST as over MCP and are listed in the hub's troubleshooting by error code; n8n sees them in the response body under error.

  • Imported fields arrive as strings: after Import cURL, "wait": "true" is a string and fails validation. Switch Specify Body to Using JSON and write true without quotes.
  • The node fails on 422: that's preflight_failed. Turn on Never Error to route on the status code, and read error.issues[].fix in the response.
  • A retried execution published nothing new and returned the old post: that's the idempotency key working. The response header Idempotent-Replayed: true confirms it.
  • 409 idempotency_key_reused: the key came from a field that repeats across items, so two different posts shared it. Add something unique per item, such as the row id plus the network.

Set up n8n in five steps

  1. 01

    Get a key

    Call POST https://socialsforai.com/v1/workspaces or sign up. The response includes an sk_ key; Free allows 100 posts a month, enough to build and test a workflow.

  2. 02

    Connect an account and copy its id

    Create a connect link with POST https://socialsforai.com/v1/connect-links, open it, approve, then GET /v1/accounts and copy the acc_ id.

  3. 03

    Add a Header Auth credential

    In n8n, create a Generic credential of type Header Auth with name Authorization and value Bearer sk_....

  4. 04

    Add the HTTP Request node

    Method POST, URL https://socialsforai.com/v1/posts, Send Body on, JSON, with accounts, text and wait: true. Add an Idempotency-Key header built from the source item's id.

  5. 05

    Test with a dry run

    Add "dry_run": true to the body, run the node once, and read the issues. Remove it when ok is true.

Questions

What developers ask

Does n8n need the MCP server?

No. The REST API is simpler from n8n and has the same features. The MCP server is for chat agents.

Is $execution.id a good idempotency key?

Only within one execution. n8n records a retry as a new execution with its own id, so build the key from the source item's id instead.

Can n8n schedule posts?

Yes. Pass scheduled_at up to 365 days ahead and we hold the post, so the workflow doesn't have to wait.

How do I post different text per network?

Use platform_options, for example {"x": {"text": "short version"}}. Accounts on other networks get the main text.

Where should the API key go in n8n?

In a Generic credential of type Header Auth rather than typed into the node, so it isn't copied around with the node's parameters.

Which networks can an n8n workflow post to today?

X, Bluesky, LinkedIn personal profiles, Mastodon and Telegram are live. Others need each network's review.

Get an API key and send your first post in five minutes.