https://app.contenthero.ai/api/v1. All endpoints take a bearer API key. Reads need no scope; writes need the scope listed.
Spaces and stages
A space is a board; each space has its own stages, in order.Cards
A card is the unit of work: its title, notes and cover, the posts that publish it (one per platform), and what it holds (its assets).
To archive a card,
POST /archive with assetType: "card". Scheduling is the scheduledAt field on PATCH /cards/{id}: it sets the card and every post, and a post can carry its own to override it.
What a card can hold
posts and assets on PATCH /cards/{id} are declarative: send the whole list you want to end up with, because anything left out is removed. Read the card first, append, then send it all back. The order of assets is the carousel order, so reordering is sending the same ids in a new sequence.
Each entry in assets either keeps one already on the card, { "id": "<asset id>" }, or adds one by exactly one reference:
An entry naming two references, or none, is refused with a
400, and so is a list that names the same post, project, file or asset id twice. An inspiration post is attached by contentId, never by its permalink: assetType: "inspiration" with an assetUrl is refused with a 400 that names contentId. A post you cannot see, or a project that is not yours, answers 404.
POST /projects and POST /projects/import take an optional cardId: the project is linked to that card in the same call, and the reply carries linkedCardId. Linking writes to the card, so it also needs planner:write; a card that is not yours answers 404 before any project is made.
GET /cards/{id} reads each kind back under the field that attached it: an inspiration asset carries contentId (with its creator, outlier score and views), and a linked project carries projectId. assetId is still returned as a deprecated alias.
Connected accounts
Encrypted credential fields on a connected account are never exposed through the API. A post names the connected account it publishes through.