Connect an Instagram account
The platform publishes approved drafts to an Instagram Business or Creator account through the Meta Graph API. It is the closest sibling of Connect a Facebook Page — same Meta app family, same Graph host, same stored credential — with three differences that shape everything below.
What connecting actually does
Section titled “What connecting actually does”The connect flow runs the Facebook Login dance and then keeps going one hop:
- authorization code → short-lived user token
- short-lived → long-lived user token (~60 days) via
fb_exchange_token - long-lived user token → the Pages you administer, asking for each Page’s
linked Instagram account in the same call
(
GET /me/accounts?fields=id,name,access_token,instagram_business_account{id,username}) - the chosen Page’s access token is stored, encrypted, on an owner-scoped
post://<ig-username>@instagram.comchannel
Only Pages that have a linked Instagram account are offered. A Page without one
is not a slow path — it is a dead end, so it never appears in the picker. If you
administer several qualifying Pages, the connect parks the long-lived user token
in a signed httpOnly cookie and returns you to
/channels?connect=choose&provider=instagram to pick; the chosen id is re-checked
against Meta before anything is stored.
The channel is named after the Instagram handle, not the Page — that is the thing you chose.
How publishing works
Section titled “How publishing works”Instagram is the one destination in the platform where publishing is two calls:
POST /{ig-user-id}/media image_url + caption → a container idPOST /{ig-user-id}/media_publish creation_id=<that id> → the published media idThere is no single-call form. A container that is created and never published
costs nothing and expires on Meta’s side, so a failure between the two steps is
safe: the card goes to failed with the error and nothing is live.
The post’s URL is then read back (GET /{media-id}?fields=permalink) rather
than derived — an Instagram permalink is built from a shortcode and the media id
is numeric. That read is best-effort: the post is already live by then, so a
failure leaves the card without a link rather than marking a published row failed.
The image must be publicly reachable
Section titled “The image must be publicly reachable”Meta fetches image_url from its own servers. Use an asset on the platform’s
public lane (GET /api/assets/:org/:visibility/:key with a public visibility), or
any other anonymously-readable https URL. A private asset produces a container
error naming the fetch, which the platform surfaces verbatim on the card — that
message is the useful one, so it is not summarised away.
Reels are not supported
Section titled “Reels are not supported”A video is a different container (media_type=REELS, video_url, plus a
status-polling loop before publish), and the publisher does not build one. The
destination declares video: none, so a video-carrying draft is refused when you
write it, not when it publishes.
1. The Meta app
Section titled “1. The Meta app”You can reuse the Meta app behind your Facebook Page connection, or use a separate one. Either way the app needs the Instagram permissions below, and a redirect URL registered for this provider:
https://platform-api.viite.ai/api/connect/instagram/callback2. Permissions
Section titled “2. Permissions”| Permission | Why |
|---|---|
pages_show_list | enumerate the Pages you administer |
pages_read_engagement | read Page data — Meta’s declared dependency, and what reads instagram_business_account |
instagram_basic | read the linked account’s id + username |
instagram_content_publish | create the container and publish it |
instagram_manage_comments | post the first comment |
3. Env
Section titled “3. Env”| Key | Class | Notes |
|---|---|---|
INSTAGRAM_CLIENT_ID | public | The Meta App ID. |
INSTAGRAM_CLIENT_SECRET | 🔒 secret | Never goes in allowed.config.types.ts. |
INSTAGRAM_OAUTH_REDIRECT_URI | public | Must byte-for-byte match a URL registered on the app. |
INSTAGRAM_LOGIN_CONFIG_ID | public, optional | Facebook Login for Business configuration id. When set, the authorize call sends config_id and no scope. |
Instagram stays dormant until all three required keys are set (plus
SOCIAL_TOKEN_ENCRYPTION_KEY), exactly like X, LinkedIn and Facebook. A partial
set logs a warning at boot and registers nothing.
Verifying what Meta actually granted
Section titled “Verifying what Meta actually granted”GET /api/connect/instagram/introspect asks Meta, via GET /debug_token, which
scopes are really on the stored token.
This matters more here than elsewhere. With INSTAGRAM_LOGIN_CONFIG_ID set, the
authorize call deliberately sends config_id and no scope, which hands the
permission list to a configuration living in the App Dashboard that Meta exposes
through no Graph endpoint. A missing instagram_content_publish is therefore
invisible: the connect succeeds, the channel row looks complete, and the first
symptom is a publish failing. Introspection is the only way to ask.
The review gate
Section titled “The review gate”instagram_content_publish and its siblings sit at Standard Access until Meta
grants Advanced Access (App Review + Business Verification, app in Live mode).
Standard Access works only for users who hold a role on the app — the same gate
that constrains the Facebook Page publisher, and the same one that decides when
this becomes usable by arbitrary customers. The code ships before the grant; the
grant is not a code change.
Troubleshooting
Section titled “Troubleshooting”| What you see | What it means |
|---|---|
| No Facebook Pages found for this account | You administer no Page. An Instagram account publishes through a Page, so there is nothing to connect yet. |
| None of the N Page(s) you administer has a linked Instagram Business or Creator account | You have Pages, but no Instagram account linked to one. Link it in the Page’s settings, then reconnect. |
| Instagram posts must carry an image | The draft was armed without one. Add an image in the post editor. |
| The image could not be fetched from the URL provided (verbatim from Meta) | image_url is not publicly reachable — usually a private asset. |
| The connected Facebook Page no longer has a linked Instagram Business or Creator account | The link was removed on Meta’s side after connecting. Re-link and reconnect the channel. |
| Instagram page tokens cannot be refreshed automatically | Meta issues no refresh token for this flow. Reconnect. |
