Developers

REST API reference

Every call in Postfjord's REST API: posts, media, the calendar, analytics, email, contacts, the Inbox, automations and landing pages, with parameters, answers and a curl example for each.

Start freeRead the help page

Before your first call

The base address is https://app.postfjord.com/api, and every path below starts with /v1. Send your key in a header on every call:

Authorization: Bearer pf_live_...

Make the key in Postfjord under Settings, Apps. A key belongs to one workspace and has one level:

  • read looks and never changes anything.
  • draft makes and edits drafts, and never gives a post a date.
  • publish may also schedule, publish, send emails and replies, and switch automations on.

A call outside the key's level answers 403. The API is included from the Solo plan.

Errors and limits

An error comes back as { "error": "<code>", "message": "..." } with a matching status:

  • 400 a field is wrong, and the message says which.
  • 401 the key is missing, wrong or revoked.
  • 403 the key's level does not allow it, or the workspace is paused or suspended.
  • 404 it does not exist in this workspace.
  • 409 the thing is in the wrong state, for example a post that is already out.
  • 429 more than 60 calls a minute for the key, or the workspace's AI budget is spent.

Retries and dry runs

Send an Idempotency-Key header with any call that makes or sends something. If a call times out, send it again with the same key: the first answer comes back with Idempotent-Replayed: true, and nothing is made twice. Keys are kept for 24 hours.

Add ?dryRun=1 to POST /v1/posts or PATCH /v1/posts/{id} to check a post without saving it. The answer says if the real call would go through and, if not, why.

Times always carry their time zone, like 2026-11-03T09:00:00-05:00. A time without one is refused.

Every operation

74 operations. Open one to see its parameters, body, answer and a curl example. The same list as a file: openapi.json.

Workspaces

GET /v1/workspaces The workspaces this key may work in

For an app connected by sign-in to all of a person's workspaces: every workspace the person is a member of, with their role and what the app may do there. For any other key: its own workspace alone. Pass an id or name from here as X-Postfjord-Workspace.

Key level: read or higher.

Answer

OK

curl https://app.postfjord.com/api/v1/workspaces \
  -H "Authorization: Bearer pf_live_..."
GET /v1/workspaces/overview All workspaces at a glance

The same workspaces as GET /v1/workspaces (at most 50), each with its empty days in the next seven days, failed posts of the last 30 days and posts in a client review. A workspace the key may not read (not an owner there, revoked there, suspended, no API on its plan) is listed with access and message and no numbers. At most 6 calls a minute per key.

Key level: read or higher.

Answer

OK

curl https://app.postfjord.com/api/v1/workspaces/overview \
  -H "Authorization: Bearer pf_live_..."
GET /v1/workspace The workspace this key belongs to

Call this first: the name confirms the right brand, the timezone is needed to turn a local time into scheduledAt, and paused says whether scheduling and sending are refused right now.

Key level: read or higher.

Answer Workspace

OK

curl https://app.postfjord.com/api/v1/workspace \
  -H "Authorization: Bearer pf_live_..."

Channels and brand

GET /v1/channels Connected social accounts with their limits

Key level: read or higher.

Answer array of Channel

OK

curl https://app.postfjord.com/api/v1/channels \
  -H "Authorization: Bearer pf_live_..."
GET /v1/brand Tone of voice, rules, themes and example posts

Key level: read or higher.

Answer BrandProfile

OK

curl https://app.postfjord.com/api/v1/brand \
  -H "Authorization: Bearer pf_live_..."

Posts

GET /v1/posts Posts in a date range or by status

from and to filter on scheduledAt. Drafts without a date come with status=draft.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
fromquery, stringInclusive. A date and time, or YYYY-MM-DD for the whole of that day in the workspace time zone.
toquery, stringInclusive. A date and time, or YYYY-MM-DD for the whole of that day in the workspace time zone.
statusquery, string
limitquery, integer

Answer array of Post

OK

curl https://app.postfjord.com/api/v1/posts \
  -H "Authorization: Bearer pf_live_..."
POST /v1/posts Create a draft, or a scheduled post when scheduledAt is given

Drafts are safe: nothing goes out until a person schedules or publishes it. A key with the draft scope gets 403 when scheduledAt is set.

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.
dryRunquery, one of 1, true1: save nothing, and answer what the call would make: { dryRun, ok, status, channels, scheduledAt, scheduledAtLocal, timezone, problems, blockers, warnings }. ok is false when the real call would be refused, with the reasons in blockers. warnings never refuse: see the post responses.

Body PostInput

FieldTypeWhat it is
baseText requiredstringThe post text. Written in the brand voice from GET /v1/brand.
channelIds requiredarray of stringIds from GET /v1/channels.
mediaIdsarray of stringIds from GET /v1/media. Instagram needs at least one image or video.
linkstringA URL to include. Ignored in the text on platforms where linkInText is false.
bioLinkstringWhere a tap on this post in Latest posts on your link in bio page goes: a product, a booking or an article. A web address with https or http, like https://example.com/shop; one without either gets https. Spaces around it are trimmed, and an IP address is refused. Never added to the post. An empty string removes it. PATCH with bioLink alone changes it on any post: drafts and cancelled posts with the draft scope, scheduled, published and partial posts with the publish scope, at most 30 changes an hour to posts that are out.
videoPageboolean, or nullfalse: this video gets no page on postfjord.page when it goes out; true or left out: the workspace setting decides. PATCH with videoPage alone switches the page of any post: drafts and cancelled posts with the draft scope, scheduled, published and partial posts with the publish scope. Turning it on for a published video without a page makes one, which counts toward the plan's video pages for the month.
targetsobjectOptional per-channel overrides: { "<channelId>": { "text": "...", "mediaIds": ["..."], "format": "...", "title": "..." } }. A Pinterest channel also takes "boardId", one of its boards from GET /v1/channels, and "title" is the pin title (up to 100 characters). mediaIds replaces the post's media on that channel ([] goes back to the post's). A LinkedIn channel takes one PDF from GET /v1/media as format "document", a swipeable carousel, with a title (the file name when left out); every other channel refuses a PDF. Picture shape and fit on this channel: "aspect" is "auto" (the default) or one of the channel's aspects from GET /v1/channels (another is refused with the list); "fit" is keyed by media id: { "<mediaId>": { "mode": "crop" or "whole", "focus": { "x": 0..1, "y": 0..1 }, "zoom": 1..4 } }. focus is the point kept in the middle (0,0 top left); left out or null, Postfjord picks the best part. "whole" cuts nothing and fills the empty space with a soft blurred copy: use it where a crop would cut people or words. null for a picture means the automatic crop. A fit replaces the channel's whole map; a fit for a picture not on the channel is dropped, a malformed one refused. Check the result with GET /v1/posts/{id}/previews before scheduling.
scheduledAtstringISO 8601 date-time ending in Z or an offset, for example 2026-11-02T09:00:00-05:00. A time without one is refused with 400. Leave out for a draft. Setting it schedules the post, which needs the publish scope.
pillarstring
intentone of give, ask, nullGive or ask. A label you set is never changed by Postfjord's automatic label; null clears it.

Answer Post

The created post. A scheduled post that asks readers to comment a word nothing answers on a channel also gets warnings, as for updatePost.

curl -X POST https://app.postfjord.com/api/v1/posts \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"baseText":"...","channelIds":["..."]}'
GET /v1/posts/{id} One post with per-channel status and permalinks

Key level: read or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Answer Post

OK

curl https://app.postfjord.com/api/v1/posts/<id> \
  -H "Authorization: Bearer pf_live_..."
PATCH /v1/posts/{id} Edit a draft or scheduled post

Send only the fields to change. scheduledAt: null turns a scheduled post back into a draft. Setting scheduledAt needs the publish scope. A body with bioLink alone changes the post's link in Latest posts, also on a published post (publish scope). A body with videoPage alone switches the post's video page, also on a published post (publish scope). When the post ends up scheduled and asks readers to comment a word to get something on a channel where nothing answers that word (no comment to DM there, or no automation that is on for the word), the answer has warnings: [{ code: noCommentDm or noAutomation, channelId, platform, word, message }]. A warning never stops the schedule.

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string
dryRunquery, one of 1, true1: save nothing, and answer what the call would make: { dryRun, ok, status, channels, scheduledAt, scheduledAtLocal, timezone, problems, blockers, warnings }. ok is false when the real call would be refused, with the reasons in blockers. warnings never refuse: see the post responses.

Body PostInput

FieldTypeWhat it is
baseText requiredstringThe post text. Written in the brand voice from GET /v1/brand.
channelIds requiredarray of stringIds from GET /v1/channels.
mediaIdsarray of stringIds from GET /v1/media. Instagram needs at least one image or video.
linkstringA URL to include. Ignored in the text on platforms where linkInText is false.
bioLinkstringWhere a tap on this post in Latest posts on your link in bio page goes: a product, a booking or an article. A web address with https or http, like https://example.com/shop; one without either gets https. Spaces around it are trimmed, and an IP address is refused. Never added to the post. An empty string removes it. PATCH with bioLink alone changes it on any post: drafts and cancelled posts with the draft scope, scheduled, published and partial posts with the publish scope, at most 30 changes an hour to posts that are out.
videoPageboolean, or nullfalse: this video gets no page on postfjord.page when it goes out; true or left out: the workspace setting decides. PATCH with videoPage alone switches the page of any post: drafts and cancelled posts with the draft scope, scheduled, published and partial posts with the publish scope. Turning it on for a published video without a page makes one, which counts toward the plan's video pages for the month.
targetsobjectOptional per-channel overrides: { "<channelId>": { "text": "...", "mediaIds": ["..."], "format": "...", "title": "..." } }. A Pinterest channel also takes "boardId", one of its boards from GET /v1/channels, and "title" is the pin title (up to 100 characters). mediaIds replaces the post's media on that channel ([] goes back to the post's). A LinkedIn channel takes one PDF from GET /v1/media as format "document", a swipeable carousel, with a title (the file name when left out); every other channel refuses a PDF. Picture shape and fit on this channel: "aspect" is "auto" (the default) or one of the channel's aspects from GET /v1/channels (another is refused with the list); "fit" is keyed by media id: { "<mediaId>": { "mode": "crop" or "whole", "focus": { "x": 0..1, "y": 0..1 }, "zoom": 1..4 } }. focus is the point kept in the middle (0,0 top left); left out or null, Postfjord picks the best part. "whole" cuts nothing and fills the empty space with a soft blurred copy: use it where a crop would cut people or words. null for a picture means the automatic crop. A fit replaces the channel's whole map; a fit for a picture not on the channel is dropped, a malformed one refused. Check the result with GET /v1/posts/{id}/previews before scheduling.
scheduledAtstringISO 8601 date-time ending in Z or an offset, for example 2026-11-02T09:00:00-05:00. A time without one is refused with 400. Leave out for a draft. Setting it schedules the post, which needs the publish scope.
pillarstring
intentone of give, ask, nullGive or ask. A label you set is never changed by Postfjord's automatic label; null clears it.

Answer Post

The updated post

curl -X PATCH https://app.postfjord.com/api/v1/posts/<id> \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"baseText":"...","channelIds":["..."]}'
DELETE /v1/posts/{id} Delete a draft or cancelled post

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Answer

OK

curl -X DELETE https://app.postfjord.com/api/v1/posts/<id> \
  -H "Authorization: Bearer pf_live_..."
POST /v1/posts/{id}/cancel Cancel a scheduled post

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Answer Post

The cancelled post

curl -X POST https://app.postfjord.com/api/v1/posts/<id>/cancel \
  -H "Authorization: Bearer pf_live_..."
GET /v1/posts/{id}/log Every publish attempt for a post

Key level: read or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Answer PublishLog

OK

curl https://app.postfjord.com/api/v1/posts/<id>/log \
  -H "Authorization: Bearer pf_live_..."
GET /v1/posts/{id}/previews The exact picture each channel of a post will send

Per switched-on channel, the file it will send for each picture, as signed links valid one hour. With a key that may write drafts, a file that is missing is made now, as publishing would make it (at most 60 per post and 500 per workspace a day, and not while the workspace storage is full). With a read-only key nothing is made: a missing file is named in errors. Show these to the person before scheduling. Nothing on the post changes.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Answer PostPreviews

OK

curl https://app.postfjord.com/api/v1/posts/<id>/previews \
  -H "Authorization: Bearer pf_live_..."
POST /v1/posts/{id}/publish Publish a post right now

Goes out to every channel on the post at once. Ask the person before calling this.

Key level: publish or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.

Answer

Result and the post

curl -X POST https://app.postfjord.com/api/v1/posts/<id>/publish \
  -H "Authorization: Bearer pf_live_..."
POST /v1/posts/bulk One action on up to 200 posts

reschedule moves scheduled posts, and drafts that have a date, by shift or so the earliest starts at startAt and the others keep their spacing. It needs the publish scope and is refused while the workspace is paused. A suspended workspace moves nothing. draft takes scheduled or cancelled posts out of the queue as drafts that keep their date, cancel cancels scheduled posts, delete removes drafts, cancelled, failed and scheduled posts (with the draft scope only drafts and cancelled posts, as DELETE /v1/posts/{id}). Posts being published or already published are never changed. Every post gets its own result: done, same (it already was what the action makes) or skipped with a code: not_found, publishing, published, status, no_date, not_scheduled, went_live, key_scope, past, month_full, x_addon, changed or error. Send a requestId of your own to make a retry move each post only once.

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.

Body

FieldTypeWhat it is
action requiredone of reschedule, draft, cancel, delete
postIds requiredarray of string
shiftobjectFor reschedule. Days keep the time of day in the workspace time zone. At most 366 days either way.
startAtstringFor reschedule, instead of shift: the new time of the earliest post, with Z or an offset.
requestIdstring

Answer

A result per post, in the order sent

curl -X POST https://app.postfjord.com/api/v1/posts/bulk \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"action":"reschedule","postIds":["..."]}'

Calendar notes

GET /v1/calendar-notes The calendar notes with a day in a range

Notes mark days for the whole workspace, such as a sale, a holiday or a course start. from and to are days in the workspace time zone, both included, at most 400 days apart.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
from requiredquery, string
to requiredquery, string

Answer

The notes, earliest first

curl https://app.postfjord.com/api/v1/calendar-notes \
  -H "Authorization: Bearer pf_live_..."
POST /v1/calendar-notes Put a note on the calendar

A note for one day (leave out to) or up to 90 days in a row. It sends nothing. A workspace keeps at most 1000 notes.

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.

Body CalendarNoteInput

FieldTypeWhat it is
titlestring
textstring
fromstring
tostringLeft out on a new note: the same day as from. At most 90 days after from.
colorone of fjord, sol, lingon, mossa, ljung, neutralfjord when left out.

Answer

The note

curl -X POST https://app.postfjord.com/api/v1/calendar-notes \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{}'
PATCH /v1/calendar-notes/{id} Change a calendar note

Send only what changes. from without to moves the whole note and keeps how many days it covers.

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Body CalendarNoteInput

FieldTypeWhat it is
titlestring
textstring
fromstring
tostringLeft out on a new note: the same day as from. At most 90 days after from.
colorone of fjord, sol, lingon, mossa, ljung, neutralfjord when left out.

Answer

The note

curl -X PATCH https://app.postfjord.com/api/v1/calendar-notes/<id> \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{}'
DELETE /v1/calendar-notes/{id} Delete a calendar note

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Answer

OK

curl -X DELETE https://app.postfjord.com/api/v1/calendar-notes/<id> \
  -H "Authorization: Bearer pf_live_..."

Media and reels

GET /v1/media Images and videos in the media library, newest first, with their processing state

Every file, whatever its state, unless status narrows it. Before 2026-09-30 only ready files were listed: send status=ready for the files a post can use now.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
limitquery, integer
statusquery, one of ready, processing, failed

Answer array of Media

OK

curl https://app.postfjord.com/api/v1/media \
  -H "Authorization: Bearer pf_live_..."
POST /v1/media Upload a picture or video to the media library

Two ways to send the file. 1) JSON { url, name?, alt? }: Postfjord downloads the file from a public http(s) address that answers with the file itself. Private and internal addresses are refused, redirects are followed up to 5 times, and the download must finish within 50 seconds. 2) The file itself as the request body, with its Content-Type, and name and alt as query parameters if you like. At most 30 MB this way: one request to Cloud Functions carries at most 32 MB, so send a bigger video as a url. JPEG, PNG, WebP, GIF, MP4 or MOV, checked on the file's own bytes. A picture is at most 50 MB and a video at most 500 MB, within the storage of the workspace's plan. At most 30 uploads a minute and 500 a day per workspace. The answer comes when the file is stored, with status processing: the thumbnails, picture size and video length follow within seconds (GET /v1/media/{id}). A draft can use the id in mediaIds straight away; scheduling a post waits until its files are ready. Send files one after another rather than many at once.

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.
namequery, stringWith the file as the body: the file name to show in the library.
altquery, stringWith the file as the body: alt text, what the picture shows.

Body MediaUpload

FieldTypeWhat it is
url requiredstringPublic http(s) address that answers with the file itself
namestringFile name to show in the library. Default: the last part of the address.
altstringAlt text: what the picture shows

Answer

Stored, and processing

curl -X POST https://app.postfjord.com/api/v1/media \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"url":"..."}'
GET /v1/media/{id} One file in the media library, with its processing state

Key level: read or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Answer Media

OK

curl https://app.postfjord.com/api/v1/media/<id> \
  -H "Authorization: Bearer pf_live_..."
POST /v1/media/{id}/resize New pictures of other shapes from one picture, each a new file in the library

Resize, like Magic Resize: the picture is rendered from its original once per shape, at up to 2160 px on the long side, and each result is saved as a new picture in the media library named after the original and the shape ("Beach day 9x16"). The original never changes. A size is a shape for the automatic crop (Postfjord picks the best part), or an object with mode crop, focus { x, y } from 0 to 1 and zoom 1 to 4, or mode whole: the whole picture on a soft blurred copy of itself, for pictures where a crop would cut people or words. At most one of each shape in a call. A picture uploaded a moment ago is waited for up to 30 seconds. The answer comes once the files are saved; each is ready a few seconds later (GET /v1/media/{id}). The new files count toward the plan's storage, and each plan makes a number of resized pictures a day (Free 24, Solo 100, Studio 250, Agency 500); over it the call is refused with resource-exhausted. When some sizes could not be made the others are kept and listed in failed.

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.

Body

FieldTypeWhat it is
sizes requiredarray of one of 1x1, 4x5, 9x16, 16x9, 1.91x1, 3x2, 2x3, 4x3 or object

Answer

Saved

curl -X POST https://app.postfjord.com/api/v1/media/<id>/resize \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"sizes":["..."]}'
POST /v1/reels Make a 9:16 video from pictures, on a new reel or story draft

Make it a reel: one picture, or up to 10, become a 1080x1920 MP4 (H.264, 30 frames a second, no sound) with a slow zoom on each picture toward the point kept in the post's fit, soft fades between them and, if you send text, a short line in the brand kit's font and colours. Send mediaIds (pictures in the library), or postId to use that post's pictures, its text and how each channel crops them. With postId this makes a story or a reel from a post that already exists, of any status: the post itself never changes. The video is 5 to 30 seconds: 6 for one picture, about 3 a picture for more, or seconds. The answer comes at once with the new media id and draft id: the draft holds the video right away, and the video is ready about a minute later (GET /v1/media/{id}, status ready; failed with error if it could not be made). A reel draft goes to the post's channels that show short videos, such as Instagram (as a Reel) and Facebook (or channelIds), a story to Instagram. Nothing is published: schedule the draft when the video is ready. Each plan makes a number of videos a day (Solo 10, Studio 25, Agency 50); over it the call is refused with resource-exhausted.

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.

Body

FieldTypeWhat it is
formatone of reel, story
mediaIdsarray of stringThe pictures, in order. Left out: the post's pictures.
postIdstringA post whose pictures, text, link and fits the video and the draft use.
textstringA short line shown on the video; the first 100 characters are used. Left out: no text.
secondsnumberHow long the video is.
channelIdsarray of stringThe channels of the new draft. Left out: the post's channels that take the format, else every one that does.
stillbooleanOnly for format story with one picture: true puts the picture itself on the story (1080x1920, ready at once, not counted as a video). The answer then has still true, status ready, jobId null and mediaId the picture.

Answer

Started

curl -X POST https://app.postfjord.com/api/v1/reels \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{}'

Analytics

GET /v1/analytics The Analytics overview: channels, top posts and best times

The numbers the app's Analytics screen shows, over 7, 30, 90 or 365 days, cut to what the plan keeps. Per channel: followers and their change, posts, likes and comments, per post and as a rate of followers, and reach per day where the platform shares it. Also the top posts across channels and the best weekday and hour to post in the workspace time zone. notes says what is missing and why.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
daysquery, one of 7, 30, 90, 365

Answer Analytics

OK

curl https://app.postfjord.com/api/v1/analytics \
  -H "Authorization: Bearer pf_live_..."
GET /v1/analytics/weekly Week by week: what the posts did, even when nobody clicked

The last 12 weeks and the week in progress (Monday to Sunday, UTC), oldest first. Per week: posts published (with the give or ask split), likes, comments, saves and shares, saves on sampled posts, profile visits, new followers and people reached where Instagram and Facebook share them, new DM conversations, new email subscribers (their own confirmation), first-heard answers per category, landing page views and taps, and link clicks in DMs. A null is a week that was not counted, not a zero. Each signal has its 4-week moving average. story is the last complete week in one sentence, and patterns say per signal whether weeks with more posts had more of it, only when at least 8 complete weeks support it. There is no ROI or attribution figure.

Key level: read or higher.

Answer WeeklySignals

OK

curl https://app.postfjord.com/api/v1/analytics/weekly \
  -H "Authorization: Bearer pf_live_..."

AI writing

POST /v1/ai/compose Ask Postfjord's composer for a draft in the brand voice

Optional. An agent can write the text itself from GET /v1/brand; this endpoint uses the workspace AI budget. At most 20 per minute per workspace.

Key level: draft or higher.

Body ComposeInput

FieldTypeWhat it is
brief requiredstringWhat the post is about, in the user's words.
channelsarray of object
mediaSummarystringWhat the chosen image or video shows.
pillarstring
tonestring

Answer ComposeResult

OK

curl -X POST https://app.postfjord.com/api/v1/ai/compose \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"brief":"..."}'
POST /v1/ideas/repurpose Turn one idea or blog post into a post per channel, an email draft and a website section, saved as drafts

Postfjord's AI writes it from the workspace AI budget, in the brand voice: one AI call for every 5 channels, run at the same time, so a request takes about as long with 12 channels as with 5. Each AI call counts toward the budget. Each post keeps the point in its own text. The posts are saved as drafts, one per channel, with a shared ideaGroupId, and the email as an email draft when email is on and email is true. The website section is returned as text only. Nothing is scheduled or sent. One request writes for at most 12 channels. If some AI calls fail, the drafts from the others are saved and failed names the channels that got none: send the same request again with the same ideaGroupId to write just those. A request that asks for the website section always writes it, since it is never saved. Send your own ideaGroupId (required for more than 5 channels) and repeat it when a request fails (after a timeout, wait 2 minutes first, since that request may still be writing), or to add more channels to the same idea: channels and the email that already have a draft in the group are not written or paid for twice, and when nothing is missing no AI call is made and reused is true. At most 5 requests a minute per workspace.

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.

Body

FieldTypeWhat it is
ideastringThe insight, tip or story. Give this, url, or both.
urlstringThe address of a blog post, article or blog feed to start from. Read through Postfjord's safe fetch.
channelIdsarray of stringConnected channels to write a post for (GET /v1/channels), at most 12.
emailbooleanAlso write an email draft. Needs email switched on for the workspace.
pagebooleanAlso suggest a section for a website or landing page (text only).
ideaGroupIdstringYour own id for this idea. Required for more than 5 channels. Repeat it on a retry so nothing is written twice.

Answer

What was written and the drafts made

curl -X POST https://app.postfjord.com/api/v1/ideas/repurpose \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{}'

Goals

GET /v1/goals The workspace's goals, newest first

Each goal with its plan, planHash, the parts a launch made (assets) and the last counted results. Goals must be open to the workspace's plan; until then the answer is 403.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
statusquery, one of proposed, launched, done, cancelled
limitquery, integer

Answer

OK

curl https://app.postfjord.com/api/v1/goals \
  -H "Authorization: Bearer pf_live_..."
POST /v1/goals Plan a goal with Postfjord's AI

Say what you want to achieve. Postfjord plans posts over the period, a comment to DM automation, a landing page with a signup form and 1 to 3 welcome emails, on the workspace's AI budget. Nothing is created except the goal itself, with status proposed. Show the plan to the person and launch it only after they say yes. missing names what the workspace lacks (dm_channel, postmail, pages, link); those parts are left out. It also names keyword when no post asks for the DM's word, and emails_no_way_in when the emails were left out because nothing could start them.

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.

Body

FieldTypeWhat it is
text requiredstringThe goal in the owner's words, e.g. "Fill 20 spots in my 6-week beginner strength program that starts November 3."
offerobject
startDatestringYYYY-MM-DD in the workspace's time zone, default today
endDatestringYYYY-MM-DD, default two weeks; at most 42 days
channelIdsarray of stringChannels to post on, default every connected channel that publishes
postCountinteger
localeone of en, sv, de, fr, es, ptThe language of the rationale, the note on why the plan looks as it does. The posts, DM, page and emails are written in the brand's content language either way.

Answer

The goal with its plan and planHash

curl -X POST https://app.postfjord.com/api/v1/goals \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"text":"..."}'
GET /v1/goals/{id} One goal with its plan, assets and results

Key level: read or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Answer

OK

curl https://app.postfjord.com/api/v1/goals/<id> \
  -H "Authorization: Bearer pf_live_..."
PATCH /v1/goals/{id} Edit a proposed goal's plan

Send the whole plan (posts, automation, page, emails) as GET returned it, with your changes. It is cleaned as the AI's plan is, and the answer has the new planHash. A keyword or page address another automation or page uses is refused. A launched goal's plan cannot change.

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Body

FieldTypeWhat it is
plan requiredobject

Answer

OK

curl -X PATCH https://app.postfjord.com/api/v1/goals/<id> \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"plan":{}}'
GET /v1/goals/{id}/results A goal's numbers: posts, DMs, contacts, confirmed, emails, clicks, page views and signups

Counted from what the parts keep, at most every five minutes.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Answer

OK

curl https://app.postfjord.com/api/v1/goals/<id>/results \
  -H "Authorization: Bearer pf_live_..."
POST /v1/goals/{id}/launch Launch an approved goal

Makes the tag, signup form, landing page, welcome emails, comment to DM automation and posts, each once: a second call finds what exists. planHash must be the one from the plan the person approved. Posts stay drafts on their dates unless postsAs is scheduled. The landing page is published when the posts are scheduled, the automation is switched on or the emails start, because they link to it; when its publish is refused, the automation and the emails stay off. The automation stays off unless turnOnAutomation is true, and the emails stay stopped unless startEmails is true. Every address is confirmed by double opt-in. Refused while the workspace is paused, and when a part would pass the plan's limits, in which case nothing is made. notes says what is off and why.

Key level: publish or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.

Body

FieldTypeWhat it is
planHash requiredstring
postsAsone of scheduled, drafts
turnOnAutomationboolean
startEmailsboolean

Answer

The launched goal and notes

curl -X POST https://app.postfjord.com/api/v1/goals/<id>/launch \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"planHash":"..."}'
POST /v1/goals/{id}/cancel Cancel a goal: stop its posts, automation, emails, form and page

Posts not yet out are cancelled, the automation switched off, the emails paused, the form switched off and the page taken offline. Contacts, tags and consents stay. Works while the workspace is paused. A Flow built step by step is turned off instead: it becomes a draft that can go live again, and its page stays online unless takePageDown is true. With mode close it closes to new signups: nobody new joins, its emails keep going to the people already in, and it stays live with closedAt set until a launch opens it again.

Key level: publish or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Body

FieldTypeWhat it is
modeone of stop, close
takePageDownboolean

Answer

OK

curl -X POST https://app.postfjord.com/api/v1/goals/<id>/cancel \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{}'

Landing pages

GET /v1/pages The link in bio page and landing pages, with views and taps

The link in bio page first, then the newest. Also the workspace's address, the plan's landing page limit, the signup forms a page can use and the styles. Landing pages must be switched on for the workspace.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
limitquery, integer
offsetquery, integer

Answer

OK

curl https://app.postfjord.com/api/v1/pages \
  -H "Authorization: Bearer pf_live_..."
POST /v1/pages Create a draft page

A link in bio page (kind bio, one per workspace) or a landing page (kind landing) at slug, within the plan's limit, drafts included. It gets the brand kit's colours on the style. Nothing goes online.

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.

Body PageInput

FieldTypeWhat it is
kindone of bio, landingCreate only. landing when left out.
titlestring
slugstringCreate, landing only: lowercase words with hyphens, like free-class.
blocksarray of PageBlockThe whole list, top to bottom. Images are ids from GET /v1/media; a form block needs a signup form id from GET /v1/pages.
descriptionstringWhat Google and shared links show.
seoTitlestring
styleone of minimal, photo, studio, editorial, launch, calm, sport, menu, letter, night
useBrandbooleanfalse keeps the style's own colours instead of the brand kit's.
liveFromstring or integer, or nullLanding pages only: when visitors can see the page. A date and time with an offset, a local time in the workspace time zone or epoch milliseconds; null removes it. Before it the address answers 404. Left out on an update: kept.
liveUntilstring or integer, or nullLanding pages only: when the page goes offline. After it visitors see that the page has ended (410). The link in bio page cannot be scheduled as a whole.
hideFromSearchbooleanLanding pages only: true keeps the page out of Google and other search engines (robots noindex, left out of the sitemap), so only people with the link find it. On a live page it changes with the next publish.

Answer PageWrite

The new draft and its checks

curl -X POST https://app.postfjord.com/api/v1/pages \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{}'
GET /v1/pages/{id} One page with its blocks, last check and numbers

Key level: read or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Answer Page

OK

curl https://app.postfjord.com/api/v1/pages/<id> \
  -H "Authorization: Bearer pf_live_..."
PATCH /v1/pages/{id} Change a page's draft

Send only what changes; blocks replaces the whole list. A published page stays live with the published version until it is published again.

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Body PageInput

FieldTypeWhat it is
kindone of bio, landingCreate only. landing when left out.
titlestring
slugstringCreate, landing only: lowercase words with hyphens, like free-class.
blocksarray of PageBlockThe whole list, top to bottom. Images are ids from GET /v1/media; a form block needs a signup form id from GET /v1/pages.
descriptionstringWhat Google and shared links show.
seoTitlestring
styleone of minimal, photo, studio, editorial, launch, calm, sport, menu, letter, night
useBrandbooleanfalse keeps the style's own colours instead of the brand kit's.
liveFromstring or integer, or nullLanding pages only: when visitors can see the page. A date and time with an offset, a local time in the workspace time zone or epoch milliseconds; null removes it. Before it the address answers 404. Left out on an update: kept.
liveUntilstring or integer, or nullLanding pages only: when the page goes offline. After it visitors see that the page has ended (410). The link in bio page cannot be scheduled as a whole.
hideFromSearchbooleanLanding pages only: true keeps the page out of Google and other search engines (robots noindex, left out of the sitemap), so only people with the link find it. On a live page it changes with the next publish.

Answer PageWrite

The draft and its checks

curl -X PATCH https://app.postfjord.com/api/v1/pages/<id> \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{}'
POST /v1/pages/{id}/publish Publish a page, with every check the app runs

Checks the page (placeholders, wording, images, forms), opens every link and asks Google Web Risk about it, then puts it live. When a check fails nothing is published and ok is false with the errors. At most 20 publishes an hour per workspace. Someone in the workspace must have accepted the page rules in the app first; until then the answer is 409 with that message.

Key level: publish or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.

Answer PagePublish

Published, or the errors that stopped it

curl -X POST https://app.postfjord.com/api/v1/pages/<id>/publish \
  -H "Authorization: Bearer pf_live_..."
POST /v1/pages/{id}/unpublish Take a page offline and keep it as a draft

Key level: publish or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Answer

OK

curl -X POST https://app.postfjord.com/api/v1/pages/<id>/unpublish \
  -H "Authorization: Bearer pf_live_..."

Email

GET /v1/mail Email overview: on or off, sender, confirmed people, emails left this month, segments

Key level: read or higher.

Answer

OK

curl https://app.postfjord.com/api/v1/mail \
  -H "Authorization: Bearer pf_live_..."
GET /v1/mail/contacts Contacts who confirmed (or are asked to confirm) the workspace's emails, 50 a page at most

Only id, name, email, consent, tags and createdAt. People who unsubscribed are never listed.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
qquery, string
tagquery, string
segmentIdquery, string
consentquery, one of confirmed, pending
limitquery, integer
cursorquery, string

Answer

A page of contacts

curl https://app.postfjord.com/api/v1/mail/contacts \
  -H "Authorization: Bearer pf_live_..."
GET /v1/mail/campaigns Email campaigns and automation emails with status and report numbers

Key level: read or higher.

Parameters

NameIn and typeWhat it is
statusquery, string
kindquery, one of campaign, automation
limitquery, integer
cursorquery, string

Answer

A page of campaigns

curl https://app.postfjord.com/api/v1/mail/campaigns \
  -H "Authorization: Bearer pf_live_..."
POST /v1/mail/campaigns Create an email draft from blocks or from a brief

Blocks are written in the workspace's brand look with its logo on top. A brief goes to Postfjord's AI and uses the workspace AI budget. Nothing is sent. With tags or segment the draft stores who it goes to, and the answer says how many confirmed contacts it reaches (reaches).

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.

Body

FieldTypeWhat it is
tagsarray of stringSend only to contacts with any of these tags, by tag name (case does not matter). Not together with segment.
segmentstringSend only to this saved segment, by name or id.
namestring
subjectstring
preheaderstring
languagestring
blocksarray of object
logoboolean
briefstring
goalstring
linksarray of string
usePostsboolean

Answer

The draft

curl -X POST https://app.postfjord.com/api/v1/mail/campaigns \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{}'
GET /v1/mail/campaigns/{id} One email with its content as simple blocks

Key level: read or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Answer

The campaign

curl https://app.postfjord.com/api/v1/mail/campaigns/<id> \
  -H "Authorization: Bearer pf_live_..."
PATCH /v1/mail/campaigns/{id} Change the subject, preheader, name, blocks or audience of a draft email

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Body

FieldTypeWhat it is
tagsarray of stringSend only to contacts with any of these tags, by tag name (case does not matter). Not together with segment.
segmentstringSend only to this saved segment, by name or id.
everyoneone of trueSend to everyone who said yes.
namestring
subjectstring
preheaderstring
blocksarray of object

Answer

The draft

curl -X PATCH https://app.postfjord.com/api/v1/mail/campaigns/<id> \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{}'
GET /v1/mail/campaigns/{id}/report The report of an email that went out, with clicks per link

Counts only. Below 10 deliveries clicks and opens are null.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Answer

The report

curl https://app.postfjord.com/api/v1/mail/campaigns/<id>/report \
  -H "Authorization: Bearer pf_live_..."
POST /v1/mail/campaigns/{id}/schedule Schedule a draft email to the audience stored on it, or to a segment

Goes to the audience stored on the draft (its tags or segment; everyone who confirmed when it has none) unless segmentId is given. The same checks as the app: sending open for the workspace, the content checks, emails left this month, review of a large first email.

Key level: publish or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.

Body

FieldTypeWhat it is
when requiredstring"now" or an ISO 8601 date-time with its time zone
segmentIdstring

Answer

Scheduled, or waiting for review

curl -X POST https://app.postfjord.com/api/v1/mail/campaigns/<id>/schedule \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"when":"..."}'
POST /v1/mail/campaigns/{id}/cancel Unschedule an email (back to a draft) or cancel one that is going out

Key level: publish or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Body

FieldTypeWhat it is
modeone of auto, unschedule, cancel

Answer

Its new status

curl -X POST https://app.postfjord.com/api/v1/mail/campaigns/<id>/cancel \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{}'
GET /v1/mail/forms The signup forms, with their public link

At most 20: id, name, the public link, whether it is on, the tag names people get when they confirm, how many signed up, its welcome automation and the goal it belongs to.

Key level: read or higher.

Answer

The forms

curl https://app.postfjord.com/api/v1/mail/forms \
  -H "Authorization: Bearer pf_live_..."
POST /v1/mail/forms Create a signup form, on at once

The answer has the public link. Everyone who signs up gets the double opt-in email first, and the tags (by name; a new name makes the tag) go on when they confirm. Needs email switched on. At most 20 forms a workspace.

Key level: publish or higher.

Parameters

NameIn and typeWhat it is
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.

Body

FieldTypeWhat it is
namestring
headingstring
textstring
buttonstring
successstring
askNameboolean
askPhonebooleanAsk for a phone number (required on the form). Needs why.
askBirthdaybooleanAsk for the date of birth (required on the form), saved as the contact birthday. Of a whole Swedish personal number only the date is kept. Needs why.
whystringWhat the phone number and date of birth are used for, shown under the fields.
phoneLabelstring
birthdayLabelstring
rewardstring
tagsarray of string

Answer

The form

curl -X POST https://app.postfjord.com/api/v1/mail/forms \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{}'
PATCH /v1/mail/automations/{id} Start, pause or resume an email automation

start turns a draft on: from then on everyone who matches its trigger gets the emails. Starting checks that sending is open, the sender, the trigger and every email, and answers 409 with what to fix. pause stops sending; resume goes on after a pause you made or one for failed checks.

Key level: publish or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Body

FieldTypeWhat it is
action requiredone of start, pause, resume

Answer

The new status

curl -X PATCH https://app.postfjord.com/api/v1/mail/automations/<id> \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"action":"start"}'
GET /v1/mail/automations Email automations that are on or paused, or all of them

The automations a key can start a person in: on or paused, and started by a tag, a segment, a form, a DM or a click. Automations that start on their own (a date, a referral reward, the email to people who went quiet) are left out.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
allquery, booleantrue adds drafts and automations that start on their own, with emails, startable, goalId, enrolled and sent on each row.

Answer

The automations

curl https://app.postfjord.com/api/v1/mail/automations \
  -H "Authorization: Bearer pf_live_..."
POST /v1/mail/automations Create an email automation, always a draft

A series of 1 to 10 emails that go by themselves to each confirmed contact once the trigger starts them: form (someone confirms through a signup form, or any form), tag (a confirmed contact gets the tag, by name; a new name makes the tag) or segment (a saved segment). The emails are written in simple blocks, as POST /v1/mail/campaigns takes them, in the brand look. The first email goes when the person joins unless it has a delay; each later one waits delayDays (default 2) or delayHours after the one before. Nobody gets an email until it is started with PATCH /v1/mail/automations/{id} (publish scope) or in the app. At most 20 a day per workspace and 50 automations in all.

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.

Body

FieldTypeWhat it is
name requiredstring
trigger requiredobject
emails requiredarray of object

Answer

The draft

curl -X POST https://app.postfjord.com/api/v1/mail/automations \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"...","trigger":{"type":"form"},"emails":[{}]}'
POST /v1/mail/automations/{id}/start Start an email automation for a person

Only for an address whose owner confirmed they want your emails, never for a contact with the tag the automation stops on, and only for an automation from GET /v1/mail/automations: one that starts on its own (a date, a referral reward, the email to people who went quiet) answers 409. A person already in it is left where they are (already: true). In a paused automation they wait until it is on. At most 500 starts a workspace and day.

Key level: publish or higher.

Parameters

NameIn and typeWhat it is
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.
id requiredpath, string

Body

FieldTypeWhat it is
email requiredstring

Answer

Whether it started

curl -X POST https://app.postfjord.com/api/v1/mail/automations/<id>/start \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"email":"..."}'

Contacts and tags

GET /v1/contacts Find contacts by email address

Every contact with the address, at most 10. Counts toward the 5,000 contacts a key may read a day.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
email requiredquery, string

Answer

The contacts with the address

curl https://app.postfjord.com/api/v1/contacts \
  -H "Authorization: Bearer pf_live_..."
POST /v1/contacts Add a contact, or update the contacts with this email address

A new contact has no email consent until the person confirms: send askToConfirm to email them the confirmation link. An address that unsubscribed, bounced or was blocked is not added (skipped). Needs the Publish scope, since a change to a contact can start an email automation. At most 2,000 contact changes a workspace and day.

Key level: publish or higher.

Parameters

NameIn and typeWhat it is
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.

Body ContactInput

FieldTypeWhat it is
email requiredstringThe address. Every contact with it is updated; when none has it, a new email contact is added.
firstNamestring
lastNamestring
fieldsobjectCustom fields by key (1 to 30 lowercase letters, digits or underscores), checked against the field types set under Contacts. null removes nothing.
askToConfirmbooleanSend the person the confirmation email (double opt-in), as Ask to confirm on the Contacts screen does. Once per address. Nobody gets your emails or automations until they click it.

Answer

The contact

curl -X POST https://app.postfjord.com/api/v1/contacts \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"email":"..."}'
POST /v1/contacts/tags Add tags to the contacts with an email address

Tags by name; a tag the workspace does not have yet is made. A tag can start an email automation for a confirmed address, so this needs the Publish scope.

Key level: publish or higher.

Parameters

NameIn and typeWhat it is
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.

Body

FieldTypeWhat it is
email requiredstring
tags requiredarray of string

Answer

The contacts and tags

curl -X POST https://app.postfjord.com/api/v1/contacts/tags \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"email":"...","tags":["..."]}'
GET /v1/tags Every tag in the workspace, by name

Key level: read or higher.

Answer

The tags

curl https://app.postfjord.com/api/v1/tags \
  -H "Authorization: Bearer pf_live_..."

Inbox and automations

GET /v1/conversations Inbox conversations, newest first

Direct message conversations on Instagram, Messenger and WhatsApp. At most 30 per page; pass nextCursor as cursor for the next page. A filtered page can be short while more remain.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
unreadquery, boolean
channelIdquery, string
tagquery, stringA tag name.
limitquery, integer
cursorquery, string

Answer

OK

curl https://app.postfjord.com/api/v1/conversations \
  -H "Authorization: Bearer pf_live_..."
GET /v1/conversations/{id} One conversation with its latest messages and whether a reply can go out

Key level: read or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, stringThe conversation id from listConversations.
limitquery, integer

Answer

OK

curl https://app.postfjord.com/api/v1/conversations/<id> \
  -H "Authorization: Bearer pf_live_..."
POST /v1/conversations/{id}/draft-reply A reply in the brand voice from Postfjord's AI, returned as text

Nothing is sent. Uses the workspace AI budget. At most 10 per minute per key.

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, stringThe conversation id from listConversations.

Body

FieldTypeWhat it is
instructionstring

Answer

The draft

curl -X POST https://app.postfjord.com/api/v1/conversations/<id>/draft-reply \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{}'
POST /v1/conversations/{id}/reply Send a reply to the person, as Send in the Inbox

Only within 24 hours of the person's last message, never to a person who opted out, at most 10 per minute per key. Refusals come back as failed-precondition with the reason.

Key level: publish or higher.

Parameters

NameIn and typeWhat it is
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.
id requiredpath, stringThe conversation id from listConversations.

Body

FieldTypeWhat it is
text requiredstring

Answer

Sent

curl -X POST https://app.postfjord.com/api/v1/conversations/<id>/reply \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"text":"..."}'
GET /v1/automations The DM automations with trigger, status and counts

Key level: read or higher.

Answer

OK

curl https://app.postfjord.com/api/v1/automations \
  -H "Authorization: Bearer pf_live_..."
POST /v1/automations Create a DM automation, always paused

Creates an automation on Instagram or Facebook that answers nobody until it is switched on with setAutomation (publish scope) or in the app. Steps run in order: message, followGate, collectEmail, followUp, delay, question, addTag ({ type: addTag, tags: [names] }; a tag name the workspace does not have is made) and aiReply (last). Steps that remove a tag, set a field or branch are added in the app. At most 10 a minute per key and 50 a day per workspace.

Key level: draft or higher.

Parameters

NameIn and typeWhat it is
Idempotency-Keyheader, stringYour own id for this one action, 1 to 128 printable ASCII characters. Send the same key again when you retry after a timeout or an error: the first answer comes back, with the header Idempotent-Replayed: true, and nothing is made or sent twice. Kept 24 hours per API key. The same key with other arguments answers 422; a second call while the first still runs answers 409. Errors are not kept, nor a publish that ended failed, partial or with ok false, so such a call can be sent again with the same key.

Body

FieldTypeWhat it is
name requiredstring
trigger requiredobject
steps requiredarray of object
publicRepliesarray of string
likeCommentboolean

Answer

OK, created paused

curl -X POST https://app.postfjord.com/api/v1/automations \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"...","trigger":{"type":"dm_keyword"},"steps":[{}]}'
PATCH /v1/automations/{id} Switch an automation on or pause it

Key level: publish or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, stringThe automation id from listAutomations.

Body

FieldTypeWhat it is
enabled requiredboolean

Answer

OK

curl -X PATCH https://app.postfjord.com/api/v1/automations/<id> \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{"enabled":true}'

Mentions

GET /v1/mentions Mentions found for the workspace, newest found first

What the Mentions tab shows: the workspace's own account tagged, reviews, and keywords found on Bluesky and Hacker News, with Postfjord's score. At most 30 per page; pass nextCursor as cursor for the next page. A filtered page can be short while more remain. Duplicates of the same text from another source show only with state=done. Mentions must be open to the workspace's plan; until then the answer is 403.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
statequery, one of new, saved, done
sourcequery, one of instagram, threads, bluesky, hn, gbp, appstore
intentquery, one of buy_intent, question, complaint, praise, comparison, other
sentimentquery, one of positive, neutral, negative
minRelevancequery, integerOnly mentions scored at least this, 0 to 100.
keywordIdquery, string
sincequery, stringOnly mentions posted at or after this time (ISO 8601 or milliseconds).
limitquery, integer
cursorquery, string

Answer

OK

curl https://app.postfjord.com/api/v1/mentions \
  -H "Authorization: Bearer pf_live_..."
GET /v1/mentions/{id} One mention with its score

Key level: read or higher.

Parameters

NameIn and typeWhat it is
id requiredpath, string

Answer

OK

curl https://app.postfjord.com/api/v1/mentions/<id> \
  -H "Authorization: Bearer pf_live_..."

Feeds for Zapier and other tools

GET /v1/feeds/contacts New contacts with an email address, newest first

Contacts in the order their address came in, from a form, an import, a DM automation or a key. People who unsubscribed are left out. Made for polling, as Zapier and Make do: only the newest rows, each with a stable id, and no cursor. Every call counts toward 10,000 polls a key and day.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
limitquery, integerHow many of the newest to return, 1 to 50. Default 25.

Answer

The newest contacts

curl https://app.postfjord.com/api/v1/feeds/contacts \
  -H "Authorization: Bearer pf_live_..."
GET /v1/feeds/signups New confirmed signups through a form, newest first

People who signed up through one of your forms and clicked the confirmation link. Made for polling, as Zapier and Make do: only the newest rows, each with a stable id, and no cursor. Every call counts toward 10,000 polls a key and day.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
limitquery, integerHow many of the newest to return, 1 to 50. Default 25.

Answer

The newest signups

curl https://app.postfjord.com/api/v1/feeds/signups \
  -H "Authorization: Bearer pf_live_..."
GET /v1/feeds/sales New sales from your connected Stripe account, newest first

Paid sales with what sent them and the contact they were credited to. Test purchases are never listed. Made for polling, as Zapier and Make do: only the newest rows, each with a stable id, and no cursor. Every call counts toward 10,000 polls a key and day.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
limitquery, integerHow many of the newest to return, 1 to 50. Default 25.

Answer

The newest sales

curl https://app.postfjord.com/api/v1/feeds/sales \
  -H "Authorization: Bearer pf_live_..."
GET /v1/feeds/posts Posts that went out, newest first

Posts published on every channel they were for, with each channel link. Made for polling, as Zapier and Make do: only the newest rows, each with a stable id, and no cursor. Every call counts toward 10,000 polls a key and day.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
limitquery, integerHow many of the newest to return, 1 to 50. Default 25.

Answer

The newest published posts

curl https://app.postfjord.com/api/v1/feeds/posts \
  -H "Authorization: Bearer pf_live_..."
GET /v1/feeds/conversations New conversations in the inbox, newest first

DMs and comments that started a conversation, with a display name and the first words of the latest message. Never an email address. Made for polling, as Zapier and Make do: only the newest rows, each with a stable id, and no cursor. Every call counts toward 10,000 polls a key and day.

Key level: read or higher.

Parameters

NameIn and typeWhat it is
limitquery, integerHow many of the newest to return, 1 to 50. Default 25.

Answer

The newest conversations

curl https://app.postfjord.com/api/v1/feeds/conversations \
  -H "Authorization: Bearer pf_live_..."

Get your workspace

Start on the free plan today. No card needed.

Start freeSee pricing