CONNECT
CONNECT
OAUTH · TOKENS · PUBLISHING · STATS

API

An HTTP API over the accounts you connected. The same call whether it goes to Instagram, Facebook, TikTok or Bluesky: Connect handles tokens, refreshing, uploading and each platform's quirks. This is the interface HEIDI Command Center talks to, and it is open to any system of your own.

Getting started
three lines

1. Get your key. You find it under Accounts, at the bottom. Every user has their own keys and sees only their own accounts. You can have several and delete one without touching the others.

2. Send it in the header X-Heidi-Key on every call.

3. The base URL is https://heidiconnect.app. Everything answers JSON, always UTF-8.

curl https://heidiconnect.app/api/me \
  -H "X-Heidi-Key: $HEIDI_KEY"

{"user": {"id": "u_...", "email": "[email protected]"},
 "accounts": 4,
 "base_url": "https://heidiconnect.app"}

The key can also be sent as ?key= in the URL, but do not do that in production: URLs end up in logs, browser history and proxy servers. The header does not.

Accounts
what is connected
GET
/api/me
Who the key belongs to and how many accounts it reaches.
GET
/api/accounts?provider=instagram
All your accounts. provider is an optional filter: instagram, facebook, tiktok, youtube, bluesky. Every account has id, username, capabilities and token_expires_in_days. The tokens themselves never leave the server.
GET
/api/accounts/{account_id}
One account, plus a fresh profile fetched from the platform. If the profile fetch fails you get profile_error instead, and the rest of the response stands.
DEL
/api/accounts/{account_id}
Disconnects the account and deletes its token here. Answers {"deleted": true}.
POST
/api/refresh-tokens
Refreshes all your tokens right away. This happens automatically every six hours, so this endpoint is for when you do not want to wait.
GET
/api/artists
The artists you set up in Studio, with persona and chosen posting route.
Publish
one call, every platform
POST
/api/post
Creates a post. See the body below.
GET
/api/post/{post_id}
The status of a post: queued, processing, published or failed, with the platform's id and URL once it is done.
GET
/api/posts?account_id=&limit=50
The log of everything you published through Connect.
POST /api/post
Content-Type: application/json
X-Heidi-Key: $HEIDI_KEY

{
  "account_id": "instagram_c176b820",
  "kind": "auto",
  "caption": "Ny låt ute nu.",
  "media": ["https://exempel.se/omslag.jpg"],
  "options": {},
  "wait": true
}

kind: auto (recommended), text, image, video, reel, story or carousel. On auto the type is chosen from what you send: one image becomes an image, one video becomes video or reel, several files become a carousel, nothing becomes text.

media: https URLs or paths to files you uploaded. Several files in the list become a carousel. The platforms fetch the file themselves, so the URL has to stay open for as long as the publish is running.

wait: true holds the call open until the platform has finished and you get status: "published" right away. false answers immediately with 202 and a post_id you can ask about whenever you want. If it takes too long with wait: true you still get 202 and a hint telling you where to fetch the status. Nothing is lost.

options: the platform specific things that fit nowhere else, for example TikTok's privacy level or whether comments should be on. An empty object is almost always enough. Instagram: first_comment posts that text as the first comment on the post (hashtags, for a clean caption); accounts without the comments permission get the text appended to the caption instead. thumb_offset (ms) picks the cover frame.

Statistics and content
reading back
GET
/api/insights/{account_id}
The account's numbers: followers, reach, views, as far as the platform hands them out.
GET
/api/content/{account_id}?limit=20
The latest posts on the account, with per post statistics where they exist. Including posts made outside Connect.
GET
/api/tiktok/creator-info/{account_id}
TikTok's own limits for the account before you post: allowed privacy levels, max video length, whether comments or duets are turned off. Answers 400 if the account is not TikTok.
Comments and messages
requires extra permissions
GET
/api/comments/{account_id}?limit=50
Comments on the account's posts.
POST
/api/comments/reply
Body: {"account_id", "comment_id", "text"}.
GET
/api/messages/{account_id}?limit=20
Conversations in the inbox.
POST
/api/messages/send
Body: {"account_id", "recipient_id", "text"}.

The code is in place, but the platforms do not let us in yet. Comments and direct messages on Instagram and Facebook require permissions we apply for in a second round with Meta. Until then these calls answer with an error from the platform, not with empty lists, so you never have to guess whether it is quiet or broken.

What each platform can do
read from the code
PlatformPublishRead and reply
Instagram image, video, reel, story, carousel statistics, posts, comments, reply, DM, send DM
Facebook text, image, video, reel, story, carousel statistics, posts, comments, reply, DM, send DM
TikTok image, video, carousel statistics, posts
YouTube video statistics, posts
Bluesky text, image, video, carousel statistics, posts, comments, reply, DM, send DM
TikTok Business image, video, carousel statistics, posts, comments, reply, like comments

The table shows what Connect can do against each platform. What the platform lets through right now is a different matter, decided by the review status at Meta and TikTok.

Errors and limits
what you can run into

Errors always come as JSON in the same shape, and the HTTP code says whose fault it is:

{"error": "Kontot instagram_x finns inte", "details": null}

401: the key is missing or wrong. 404: the account or the post does not exist, or belongs to someone else. 400: bad body, and details points out exactly which field is off. 502: the platform answered with an error, and error is the platform's own message in plain text.

Limits

  • Uploaded media files are cleaned out after 48 hours. Publish first, clean up after.
  • Publishing with wait: true gives up after 600 seconds and hands over to post_id.
  • Tokens are refreshed automatically every six hours. You never have to do it yourself.
  • Sign in by email link is rate limited. API keys are not, they are yours.
  • A paid seat can create 1000 posts per calendar month. Queued, processing and published posts count, failed ones do not. Past the cap POST /api/post answers 429 with details.kvot and the date the quota resets. GET /api/me shows kvot: anvant (used), tak (cap), kvar (left), aterstalls (reset date). Command Center seats are not capped.

One account, one owner

Every call only reaches accounts that belong to the key's user. There is no way to reach someone else's accounts with your own key, not even with the right account_id: ownership is checked in the database query, not afterwards.

If a key leaks
do this

Delete it under Accounts and create a new one. The old one stops working immediately, and your connected accounts are untouched, so you do not have to reconnect anything. If you run several systems it is worth giving each system its own key: then you can pull one out without shutting down the rest.

CONNECT
not logged in public address https://heidiconnect.app Heidi-API https://heidiconnect.app/api
HEIDI CONNECT · BUILD V0.1 · HEIDI CONNECT