Integrations
Create and schedule posts from n8n with the HTTP Request node, and start workflows from Pozy webhooks with a verified signature.
n8n talks to Pozy through its built-in nodes: HTTP Request for actions and Webhook for triggers. This works on n8n Cloud and self-hosted n8n, with no community node to install.
You need a Pozy API key (Developers → API keys in the dashboard) and at least one connected account.
#Create the credential
#Add a Bearer credential
In n8n open Credentials → Add credential and choose Bearer Auth. Paste your pz_live_ key as the token and name it "Pozy".
#Check it
Add an HTTP Request node with:
- Method: GET
- URL:
https://api.pozy.ai/v1/accounts - Authentication: Generic Credential Type → Bearer Auth → Pozy
Click Execute step. The output lists your connected accounts. Note the _id of each account you want to post to.
If your n8n version has no Bearer Auth type, use Header Auth with the name Authorization and the value Bearer pz_live_....
#Create a post
Add an HTTP Request node:
| Field | Value |
|---|---|
| Method | POST |
| URL | https://api.pozy.ai/v1/posts |
| Authentication | Generic Credential Type → Bearer Auth → Pozy |
| Send Headers | On. Name Idempotency-Key, value {{ $execution.id }}-{{ $itemIndex }} |
| Send Body | On, Body Content Type JSON, Specify Body Using JSON |
Body, with values mapped from earlier nodes:
{
"content": "{{ $json.text }}",
"platforms": [
{ "platform": "linkedin", "accountId": "6650f1c2a8b4e3d2c1b0a333" }
],
"mediaItems": [
{ "type": "image", "url": "{{ $json.imageUrl }}" }
],
"publishNow": true
}Drop mediaItems for a text-only post. The response contains post._id and, per platform, status and platformPostUrl once it is live.
When you put {{ $json.text }} inside a JSON string, a quote or a line break in the text breaks the JSON. Map the whole value instead: "content": {{ JSON.stringify($json.text) }}, without surrounding quotes.
#Schedule for later
Replace publishNow with a time and a timezone. Compute the time in a Code node before the request:
const when = new Date();
when.setDate(when.getDate() + 1);
const day = when.toISOString().slice(0, 10);
return [{ json: { ...$json, scheduledFor: `${day}T09:00:00` } }];{
"content": {{ JSON.stringify($json.text) }},
"platforms": [{ "platform": "linkedin", "accountId": "6650f1c2a8b4e3d2c1b0a333" }],
"scheduledFor": "{{ $json.scheduledFor }}",
"timezone": "Europe/Madrid"
}A time without an offset is read in timezone, so this posts at 9:00 in Madrid tomorrow. See Scheduling and timezones.
#Post a file
Posts take media as URLs. For a file that only exists inside n8n (from Google Drive, an email attachment or a generated image):
- HTTP Request:
POST https://api.pozy.ai/v1/media/presignwith body{ "filename": "photo.jpg", "contentType": "image/jpeg" }. KeepuploadUrlandpublicUrlfrom the response. - HTTP Request:
PUTto{{ $json.uploadUrl }}, Authentication None, Body Content Type n8n Binary File, with headerContent-Type: image/jpeg. - Use
publicUrlinmediaItemsof the create-post request.
The upload URL goes straight to storage, so it must not carry your Pozy key. See Media uploads.
#Start a workflow from a Pozy event
#Add a Webhook node
Set HTTP Method to POST and Respond to Immediately. Under Options, turn on Raw Body so you can verify the signature. Copy the Production URL, then activate the workflow.
#Point Pozy at it
Create the endpoint in the dashboard under webhooks, or with one more HTTP Request node:
{
"name": "n8n alerts",
"url": "https://your-n8n.example.com/webhook/pozy-events",
"events": ["post.failed", "account.disconnected"]
}Copy the endpoint's secret from the response.
#Verify and route
Add a Code node after the Webhook node:
const crypto = require("crypto");
const secret = "whsec_..."; // the endpoint secret
const raw = await this.helpers.getBinaryDataBuffer(0, "data");
const headers = $input.first().json.headers;
const expected = crypto.createHmac("sha256", secret).update(raw).digest("hex");
const given = headers["x-pozy-signature"] ?? "";
if (given.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected))) {
throw new Error("Bad signature");
}
const event = JSON.parse(raw.toString("utf8"));
return [{ json: { eventId: headers["x-pozy-event-id"], ...event } }];Then branch with a Switch node on {{ $json.event }}.
On self-hosted n8n, the Code node can only load crypto when the instance allows it: set NODE_FUNCTION_ALLOW_BUILTIN=crypto. Keep the secret in an environment variable or n8n credential rather than in the node where you can.
Pozy retries a delivery that does not get a 2xx within 5 seconds, so the same event can arrive twice. If a duplicate would hurt (an email, a Slack alert), store eventId and skip ids you have seen.
#Workflow ideas
- Content calendar. Schedule Trigger every morning, Google Sheets reads today's rows, HTTP Request creates one post per row with
Idempotency-Keyset to the row id. - RSS to social. RSS Feed Read on your blog, IF for new items, HTTP Request creates a LinkedIn post and a shorter X post with
customContentper platform. - Failure alerts. Webhook on
post.failed, then Slack or email withpost.platforms[].error. - Weekly report. Schedule Trigger on Mondays,
GET /v1/analytics/dashboard?fromDate=...&toDate=..., then an email with the totals and top posts.
#Troubleshooting
| Symptom | Cause |
|---|---|
401 with invalid_api_key |
The credential is not attached to the node, or the key was revoked |
400 with param set |
The body is missing a field or a value is wrong; the error text says which |
| JSON parse error in the node | A mapped value broke the JSON. Use JSON.stringify(...) for text values |
429 with rate_limit_exceeded |
Too many requests in a minute. Add a Wait node in loops, or enable Batching in the node's options |
| Signature never matches | Raw Body is off, or the secret is from a different endpoint |