All integrations

Planly

MARKETING · MARKETING

Social channels, scheduled posts, media library, and AI credits in the account they connected.

Acts as the person, not as itself

Each user connects their own account. Every call carries both identities — the agent and the person it is acting for — so the agent can never reach past what that individual can already do.

Credentials never touch the agent

Tokens live in the vault and attach server-side at call time. The agent holds a session, not a secret, and revoking access does not mean rotating a key.

Every call on the record

Who asked, which agent acted, which action ran, and the verdict that let it through — one audit trail across every integration, not one per vendor.

What an agent can do

Each action is granted on its own. An agent allowed to read is not thereby allowed to write, and the scope beside each row is what the acting user must have connected for it to run at all.

planly_get_ai_creditsREAD

Read how many Planly AI credits a team has left, via GET /api/v2/ai/credits. Planly's own words: 'Get available AI credits left in a team.' The reply is `{"data": <integer>}` -- the remaining count and nothing else. Call it before a run of 'Complete a text prompt', which spends from this balance; there is no other way to see it. `teamId` comes from 'List teams'.

api
planly_get_channels_deleteWRITE

Disconnect one social channel from its team, via GET /api/v2/channels/delete. PERMANENT AND DESTRUCTIVE, and note the verb: Planly serves this deletion over **GET** with the channel `id` in the query string. That is the vendor's own design, documented that way, not a transcription error. It answers `{"data": true, "error": null}`, which says the call was accepted and names nothing, so confirm with 'List channels'. Reconnecting the channel afterwards means running the social network's own authorization again inside the Planly app -- this API cannot do it.

api
planly_post_ai_completeWRITE

Send a text prompt to Planly's AI and get completions back, via POST /api/v2/ai/complete. Planly's own words: 'Complete text prompt.' The reply carries `data.id` and `data.choices[].text`, one entry per requested result. THIS SPENDS THE TEAM'S AI CREDITS -- read the balance first with 'Get available AI credits'. `teamId`, `prompt` and `n` are all required.

api
planly_post_channels_listREAD

List the social channels connected to a team, via POST /api/v2/channels/list. Each row carries the channel's `id` -- the value every schedule's `channelId` takes -- plus its `social_network`, `name`, `picture`, `url`, `type`, `is_group`, numeric `status` and how many `schedules` it holds. THE `granted_scopes` AND `declined_scopes` FIELDS BELONG TO THE SOCIAL NETWORK, not to Planly and not to Agentic Fabriq: they record what Facebook, TikTok or LinkedIn granted Planly when the channel was connected, which is why a channel can be present and still refuse a post. Set `excludeCompetitors` to true to drop competitor-type channels. NOTE THE SPELLING: this is the one operation on this surface whose team argument is `team_id` rather than `teamId`.

api
planly_post_media_deleteWRITE

Delete media from a team's library, via POST /api/v2/media/delete. PERMANENT AND DESTRUCTIVE, and it takes an ARRAY: `ids` is a list of media ids and every one of them goes in a single call. It answers `{"data": true, "error": null}`, which says the call was accepted and names nothing, so confirm with 'List media'. A schedule that has not published yet and points at deleted media loses its attachment.

api
planly_post_media_finish_uploadWRITE

Finalise a direct media upload, via POST /api/v2/media/finish-upload. STEP THREE OF THREE: call it after your client has PUT the bytes to the `uploadUrl` that 'Start a media upload' returned, passing back the same `mediaId`. Planly's own words: 'This step is necessary to confirm that the file was successfully uploaded and to finalize its storage on the server.' It answers with the stored media's metadata -- `contentUri`, `thumbnailUri`, `resolution`, `duration` for video, `createdBy` and `createdAt` -- and the `id` that a schedule's `media[].id` takes.

api
planly_post_media_import_from_urlWRITE

Have Planly fetch a media file from a public URL and store it in a team, via POST /api/v2/media/import-from-url. The ONE-CALL alternative to the three-step direct upload, for a file that is already on the web. Planly's supported content types are `video/mp4`, `image/png`, `image/jpeg` and `image/webp`. The reply is the same metadata shape 'Finish a media upload' returns, including the `id` that a schedule's `media[].id` takes. `teamId` and `url` are both required.

api
planly_post_media_listREAD

List a team's media library, via POST /api/v2/media/list. Planly's own words: 'This endpoint retrieves medias in a specified team.' CURSOR-PAGED: the reply's `data.next` is the value to send back as `pagination.cursor` on the following call, `pagination.pageSize` defaults to 50, and `pagination.orderBy` is a TWO-ELEMENT ARRAY -- index 0 is `CreatedAt` or `ContentLength`, index 1 is `asc` or `desc`. `data.totalNumberOfRows` is the full count and `data.rows[]` the page. `teamId` is required.

api
planly_post_media_start_uploadWRITE

Begin a direct media upload and get a pre-signed URL to send the bytes to, via POST /api/v2/media/start-upload. STEP ONE OF THREE, AND THE BYTES DO NOT TRAVEL THROUGH AGENTIC FABRIQ: Planly answers with a `mediaId`, an `uploadUrl`, the `verb` to use (PUT) and the `headers` to send, and your own client uploads the file to that URL before you call 'Finish a media upload'. `teamId` and `contentLength` are required, and you must supply at least one of `contentType` or `fileName` -- when both are sent Planly honours only `contentType`. Planly's own warning: `contentLength` IS VALIDATED during the upload, so declaring 10 MB and then sending 15 MB is rejected. If the file is already reachable on the web, 'Import media from a URL' does the whole thing in one call instead.

api
planly_post_pinterest_get_board_list_by_channelidREAD

List the Pinterest boards a connected Pinterest channel can pin to, via POST /api/v2/pinterest/get-board-list/{channelId}. Planly's own words: 'Use this to get board IDs when creating schedules with Pinterest options (the boardId field is required when publishing).' Each entry in `data.boards` is a `{label, value}` pair whose `value` IS the board id. `channelId` is the PLANLY channel id from 'List channels' -- not a Pinterest account id -- and the channel has to be a Pinterest one.

api
planly_post_schedule_groups_createWRITE

Create or UPDATE a group of scheduled posts, via POST /api/v2/schedule-groups/create. Planly's own words: 'Providing an `id` will update the existing group, while omitting it will create a new group' -- so this one tool is both the create and the update, and sending an id REPLACES that group's schedules. A group publishes one piece of content across several channels at one `publishOn`; omit `publishOn` and Planly publishes immediately. `status` is `draft` or `scheduled` (the default). Each `schedules[]` entry needs a `channelId` and carries `content`, `media[]` and a per-network `options` object -- see that argument for the nine documented network shapes. SET `validateOnly` TO TRUE TO DRY-RUN IT: Planly validates the group and creates nothing, which is how to check a post before it goes out. `teamId` and `scheduleGroups` are required.

api
planly_post_schedules_createWRITE

Create scheduled posts one channel at a time, via POST /api/v2/schedules/create. The flat sibling of 'Create or update a schedule group': `schedules` is an array of posts, each with its own `channelId`, `content`, `media[]`, per-network `options` AND ITS OWN `publishOn` -- omit `publishOn` on an entry and that post publishes immediately. Use this when the posts are independent; use the schedule-group tool when one piece of content goes out across channels at one time, or when you need to UPDATE an existing group, which this tool cannot do. Planly's own note: 'The response is identical to /schedule-groups/create'.

api
planly_post_schedules_listREAD

List a team's scheduled and published posts, via POST /api/v2/schedules/list. Planly's own words: 'This endpoint retrieves schedules in a specified team. For pagination, include `next` field returned from response in next request's `pagination.cursor` field.' Each row carries the post's `content`, its `channel` summary, `publish_on`, `created_at`, a numeric `status`, the published `url` and `external_id`, and `error_message`. READ `error_message`: a post the social network REJECTED still appears in this list, and that field is the only thing that says so. `pagination.pageSize` defaults to 50. `teamId` is required.

api
planly_post_team_createWRITE

Create a new Planly team, via POST /api/v2/team/create. Takes a `name` and answers `{"data": {"teamId": "..."}}` -- that id is what every other tool on this surface takes as its team argument. A team is the container for channels, media and schedules, and the account this connection authenticates as becomes its owner.

api
planly_post_team_deleteWRITE

Delete a Planly team, via POST /api/v2/team/delete. PERMANENT AND DESTRUCTIVE, and the widest destruction on this surface: the team's channels, media library and schedules go with it. Note the shape -- it is a **POST** whose `id` rides in the QUERY STRING rather than in a body, which is Planly's own design and is documented that way. It answers a bare `true`, which says the call was accepted and names nothing, so confirm with 'List teams'.

api
planly_post_team_getREAD

Read one team's record, via POST /api/v2/team/get. Returns the team's `name` and `picture`, your numeric `role`, `channels_count`, `disconnected_channels_count` and `members_count`, plus the two objects worth reading before a bulk schedule: `limits`, which carries the plan's caps including the monthly schedule limit per channel, and `permissions`. `disconnected_channels_count` is the field that explains a schedule which will not publish. `id` is the TEAM id, from 'List teams'.

api
planly_post_team_transfer_ownershipWRITE

Hand ownership of a team to another of its members, via POST /api/v2/team/transfer-ownership. IRREVERSIBLE BY THE CALLER: once it succeeds the connected account is no longer the owner and cannot transfer it back -- only the new owner can. It answers `{"data": "Successfully Transferred", "error": null}`. `teamId` and `userId` are both required, and `userId` must already be a member of the team ('List team users').

api
planly_post_team_usersREAD

List the members of a team, via POST /api/v2/team/users. Each row carries the member's `id`, `fullname`, `email`, `picture` and numeric `role`, and that `id` is what 'Transfer team ownership' and 'Remove a user from a team' take as `userId`. `id` on this call is the TEAM id, not a user id.

api
planly_post_team_users_removeWRITE

Remove a member from a team, via POST /api/v2/team/users/remove. DESTRUCTIVE: the person loses access to the team's channels, media and schedules the moment it succeeds, and putting them back is a new invitation from inside the Planly app rather than anything this API offers. It answers `{"data": "User removed from team", "error": null}`. `teamId` and `userId` are both required; `userId` comes from 'List team users'.

api
planly_post_teams_listREAD

List every Planly team this connection belongs to, via POST /api/teams/list. Planly's own words: 'This endpoint retrieves all teams that you belong.' CALL THIS FIRST -- almost every other tool here needs a team id, and this is where it comes from. Each row carries the team's `id`, `name`, `picture`, your numeric `role`, and how many `channels` and `users` it holds. TWO THINGS ARE TRUE OF THIS ROUTE AND OF NO OTHER. It is the only operation Planly serves OUTSIDE its v2 API: its address is `/api/teams/list`, and `/api/v2/teams/list` answers 404 (measured 2026-09-25). And it answers HTTP **200** when the credential is refused, with `{"error":{"code":401,"message":"Authentication required"}}` in the body -- for a bogus key and for no Authorization header at all, while every v2 route answers a true 401 (measured 2026-09-25). Agentic Fabriq raises that envelope as the 401 it says it is rather than returning it as a result, so a dead key reads here as a dead key instead of as an empty team list.

api

Put Planly behind one governed endpoint.

Same permissions, same audit trail, whatever else you connect next.