Instagram Integration
Connect your agent to one or more Instagram professional accounts with a recommended Meta System User token, or one account through advanced customer-owned Instagram OAuth.
Overview
The Instagram integration supports two official Meta paths. Recommended direct mode uses a Business Manager System User and the Facebook Pages linked to Instagram Business or Creator accounts. It supports selecting one or more discovered professional accounts. Advanced mode uses Instagram API with Instagram Login directly, authorizes one account per OAuth connection, and does not require a Facebook Page. Both paths keep tokens server-side and publish through Graph API v26.
The two token types use different official hosts: System User mode derives a Facebook Page access token and publishes through graph.facebook.com/v26.0; Instagram Login issues an Instagram User access token and publishes through graph.instagram.com/v26.0. Wiro selects the matching host automatically.
Skills that use this integration:
int-instagram-post— Publish feed carousels, Reels, and Stories; any agent can also post a finished video as a Reel
Agents that typically enable this integration:
- Social Manager
- Video Creator — to post the videos it makes as Reels. Add
int-instagram-postwithSkillsApply; it adds $1 a month to the Starter price. Video Creator then posts a Reel when the request asks for it or anInstagramline in itsvideo-preferencessays to, and otherwise offers to. - Any custom agent that needs Instagram publishing
Availability
| Mode | Status | Notes |
|---|---|---|
"api_key" |
Recommended | Enter a Business Manager System User token. Wiro discovers eligible linked Instagram professional accounts without browser OAuth. |
"own" |
Advanced | Use your own Meta Business app and Instagram Login OAuth. This path does not require a linked Facebook Page. |
"wiro" |
Coming soon | Wiro's shared Meta App is under review. |
Prerequisites
- A Wiro API key — Authentication.
- A deployed agent — Agent Overview; keep the
useragents[0].guid. - A Meta Business account — business.facebook.com.
- One or more Instagram Business or Creator accounts — personal accounts cannot use content publishing.
- Recommended direct mode: a System User, the Business app used to generate its token, and a Facebook Page linked to each Instagram professional account you want the agent to use.
- Advanced OAuth mode: a Meta Developer account, an app configured for Instagram Login, and an HTTPS callback URL. A Facebook Page is not required for this mode.
Preparing the Instagram account
For recommended System User mode:
- In the Instagram mobile app: Settings → Account → Switch to Professional Account → pick Business or Creator.
- In Meta Business Suite: select the Facebook Page that should own the Instagram account → Settings → Linked accounts → Instagram → Connect account. Sign in with the Instagram account and grant manage permissions.
Without both steps, Facebook Graph API cannot discover the professional account from the System User's assigned Pages.
For advanced Instagram Login OAuth, only step 1 is required. Meta's Instagram Login flow accesses the professional account directly and does not require a Facebook Page.
Recommended Setup: System User Token
This is the dashboard's default path. Wiro uses the System User token only on the server to discover assigned Pages, find their linked Instagram professional accounts, and derive the Page access token required by the Facebook Login form of Instagram Content Publishing.
Step 1: Assign the Page and Instagram assets
- Complete Preparing the Instagram account.
- Open Meta Business Settings → Users → System Users.
- Create or select a System User.
- Assign every linked Facebook Page the agent may use with
CREATE_CONTENT. - Assign each Instagram account's Content task.
- Select Generate new token and choose the Business app used for this integration.
- At Set expiration, choose Never when Meta offers it. If Meta requires an expiring token for the business, regenerate and reconnect before expiry.
- Grant:
business_managementinstagram_basicinstagram_content_publishpages_show_listpages_read_engagementads_readads_management
Meta's Page-linked publishing contract requires both ads permissions when the Page role is granted through Business Manager.
If a permission does not appear, add the matching app use case through Meta's use-case permission mapping. The Business app must not require appsecret_proof on every request because direct mode does not collect its App Secret.
Step 2: Save the token
curl -X POST "https://api.wiro.ai/v1/UserAgent/CredentialUpsert" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"useragentguid": "your-useragent-guid",
"fields": [
{ "credentialkey": "instagram", "fieldname": "authmethod", "fieldvalue": "api_key" },
{ "credentialkey": "instagram", "fieldname": "systemusertoken", "fieldvalue": "YOUR_SYSTEM_USER_ACCESS_TOKEN" }
]
}'
The System User token is encrypted, write-only, excluded from the agent runtime, and never returned by customer-facing credential endpoints.
Step 3: Validate and discover Instagram accounts
curl -X POST "https://api.wiro.ai/v1/UserAgentOAuth/OAuthConnect" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"useragentguid": "your-useragent-guid",
"credentialkey": "instagram",
"authmethod": "api_key"
}'
Response:
{
"result": true,
"accounts": [
{ "id": "17841400000000000", "name": "my_brand" }
],
"errors": []
}
Only public IDs and usernames are returned. The System User and derived Page tokens stay server-side.
Step 4: Select the Instagram accounts
Instagram is a multi-select picker in recommended System User mode. Send one or more entries from the latest discovery response:
curl -X POST "https://api.wiro.ai/v1/UserAgentOAuth/SetPickerAccounts" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"useragentguid": "your-useragent-guid",
"credentialkey": "instagram",
"accounts": [
{ "accountId": "17841400000000000", "igusername": "my_brand" },
{ "accountId": "17841400000000001", "igusername": "my_second_brand" }
]
}'
Call this within 15 minutes of OAuthConnect. Wiro matches the selection against its server-side discovery result and stores each derived Page-scoped runtime token. The dashboard auto-selects a sole result; API clients must still call this endpoint explicitly. If several linked professional accounts are returned, the dashboard opens a checkbox picker and saves every selected account with its own server-side Page token.
Step 5: Verify the connection
Call POST /UserAgentOAuth/OAuthStatus with { "useragentguid": "...", "credentialkey": "instagram" }. A successful direct connection returns every selected numeric account ID and username in accounts[], connected: true, and an empty tokenexpiresat.
Advanced Setup: Customer-Owned Instagram OAuth
Step 1: Create a Meta Developer App
You may reuse a compatible Business app that already has the Instagram product configured.
- developers.facebook.com/apps → Create app → Other → Business.
- App display name, contact email, Business Account → Create app.
- Leave in Development Mode.
Step 2: Add the "Instagram" product
- From the app dashboard, Add product.
- Find "Instagram" (not "Instagram Basic Display" — that's for personal accounts and is being deprecated).
- Set up.
Step 3: Configure the OAuth redirect URI
- Left sidebar: Instagram → API setup with Instagram login.
- Scroll to Business login settings → OAuth settings.
-
Add to Valid OAuth Redirect URIs:
https://api.wiro.ai/v1/UserAgentOAuth/IGCallback - Save changes.
Note: Instagram OAuth has its own authorize URL at instagram.com/oauth/authorize (not facebook.com/…), but the redirect URI is still registered inside the Meta Developer App.
Step 4: Note the required permissions
Wiro requests these exact scopes:
instagram_business_basic,instagram_business_content_publish,instagram_business_manage_messages,instagram_business_manage_comments,instagram_business_manage_insights
| Permission | Why |
|---|---|
instagram_business_basic |
Basic account info, profile data. |
instagram_business_content_publish |
Publish feed, carousel, reel, and story content. |
instagram_business_manage_messages |
Read and reply to DMs (used by some skills). |
instagram_business_manage_comments |
Read, reply, hide, delete comments. |
instagram_business_manage_insights |
Read engagement insights for posts and profile. |
These work without App Review in Development Mode for any Facebook user in App Roles. No pages_* scopes are requested — Instagram Login uses its own scope family.
Step 5: Copy your App ID and App Secret
App settings → Basic → copy App ID, click Show → copy App Secret.
Step 6: Add users as Testers (only if needed)
If the connecting person is not the app Admin, add them under App Roles → Roles → Add People → Testers and have them accept before starting OAuth.
Step 7: Save your Meta App credentials to Wiro
curl -X POST "https://api.wiro.ai/v1/UserAgent/CredentialUpsert" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"useragentguid": "your-useragent-guid",
"fields": [
{ "credentialkey": "instagram", "fieldname": "appid", "fieldvalue": "YOUR_META_APP_ID" },
{ "credentialkey": "instagram", "fieldname": "appsecret", "fieldvalue": "YOUR_META_APP_SECRET" },
{ "credentialkey": "instagram", "fieldname": "authmethod", "fieldvalue": "own" }
]
}'
Step 8: Initiate OAuth
curl -X POST "https://api.wiro.ai/v1/UserAgentOAuth/OAuthConnect" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"useragentguid": "your-useragent-guid",
"credentialkey": "instagram",
"redirecturl": "https://your-app.com/settings/integrations",
"authmethod": "own"
}'
Response:
{
"result": true,
"authorizeUrl": "https://www.instagram.com/oauth/authorize?client_id=...&redirect_uri=...&scope=instagram_business_basic%2Cinstagram_business_content_publish%2Cinstagram_business_manage_messages%2Cinstagram_business_manage_comments%2Cinstagram_business_manage_insights&response_type=code&state=...",
"errors": []
}
Step 9: Handle the callback
After consent, Wiro exchanges the code for a short-lived token, upgrades it to a long-lived token via graph.instagram.com/access_token?grant_type=ig_exchange_token, fetches the Instagram user info, and redirects the user back.
Success URL:
https://your-app.com/settings/integrations?ig_connected=true&ig_username=my_brand
Parse:
const params = new URLSearchParams(window.location.search);
if (params.get("ig_connected") === "true") {
const username = params.get("ig_username");
showSuccess(`Connected @${username}`);
} else if (params.get("ig_error")) {
handleError(params.get("ig_error"));
}
Advanced Instagram Login OAuth has no secondary selection step. The authorized Instagram professional account is written directly by the callback as a one-entry account/token array, matching the same runtime contract as System User mode. The SetPickerAccounts step applies only to recommended System User mode.
Step 10: Verify the connection
curl -X POST "https://api.wiro.ai/v1/UserAgentOAuth/OAuthStatus" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"useragentguid": "your-useragent-guid",
"credentialkey": "instagram"
}'
{
"result": true,
"connected": true,
"accounts": [
{ "id": "17841400000000000", "name": "my_brand" }
],
"connectedat": "2026-04-17T12:00:00.000Z",
"tokenexpiresat": "2026-06-16T12:00:00.000Z",
"errors": []
}
connected: truerequires the active mode's validated secret plus the selected or authorized Instagram account.- Each
accounts[].idis a selected numeric Instagram professional account ID. - Each
accounts[].nameis its Instagram username without@. - Recommended System User mode can return multiple entries. Advanced Instagram Login OAuth returns one entry because one account is authorized per consent.
tokenexpiresatis empty for direct System User mode and approximately 60 days for customer-owned Instagram OAuth.- No
refreshtokenexpiresat— Instagram does not expose a separate refresh token for this connection.
Step 11: Start the agent if it's not running
curl -X POST "https://api.wiro.ai/v1/UserAgent/Start" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{ "guid": "your-useragent-guid" }'
API Reference
POST /UserAgentOAuth/OAuthConnect
| Parameter | Type | Required | Description |
|---|---|---|---|
useragentguid |
string | Yes | Agent instance GUID. |
credentialkey |
string | Yes | "instagram". |
redirecturl |
string | OAuth only | HTTPS URL (or localhost/127.0.0.1 for dev). Direct System User validation does not use it. |
authmethod |
string | No | "api_key" for recommended System User mode, "own" for customer-owned Instagram OAuth, or "wiro" when Wiro-managed OAuth becomes available. Send the value explicitly. |
OAuth response: { result, authorizeUrl, errors }. Direct response: { result, accounts: [{id, name}], errors } with no authorizeUrl.
GET /UserAgentOAuth/IGCallback
Server-side. Query params appended to your redirecturl:
| Param | Meaning |
|---|---|
ig_connected=true |
OAuth succeeded. |
ig_username |
Connected Instagram handle (without @). |
ig_error=<code> |
Failure. |
The callback path is per-provider — Instagram's stays IGCallback.
POST /UserAgentOAuth/SetPickerAccounts
Used only by direct System User mode after OAuthConnect discovery.
| Parameter | Type | Required | Description |
|---|---|---|---|
useragentguid |
string | Yes | Agent instance GUID. |
credentialkey |
string | Yes | "instagram". |
accounts |
array | Yes | One or more { accountId, igusername? } entries from the latest discovery response. Do not supply access tokens. |
Call within 15 minutes of direct discovery. The response contains { result, accounts: [{id, name}], errors } and restarts a running agent after the selection is persisted.
POST /UserAgentOAuth/OAuthStatus
Body: { useragentguid, credentialkey: "instagram" }. Response: connected, accounts: [{id, name}] where id is the numeric account ID and name is the username, connectedat, and tokenexpiresat.
POST /UserAgentOAuth/OAuthDisconnect
Body: { useragentguid, credentialkey: "instagram" }. Clears the active token and selected account without remote revocation. Direct mode also clears systemusertoken; customer-owned OAuth App ID and App Secret are preserved for reconnection.
Token lifecycle
Wiro refreshes renewable customer-owned Instagram OAuth tokens while the agent is running. Direct mode uses a System User token plus a derived Page token with no fixed expiry recorded by Wiro. Meta can still invalidate either token when permissions, asset assignments, or account security change; expiring System User tokens must be regenerated before Meta's stated expiry. If OAuthStatus reports connected: false, reconnect using the active mode.
Using the Skill
Once Instagram professional accounts are connected, the agent uses int-instagram-post to publish feed carousels, reels, and stories. A request must identify one account when multiple are connected; the agent never guesses or publishes to all accounts from an ambiguous instruction. To adjust the Social Manager's bundled cs-cron-social-content-scanner schedule, call POST /UserAgent/CustomSkillUpsert with enabled and interval only:
curl -X POST "https://api.wiro.ai/v1/UserAgent/CustomSkillUpsert" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"useragentguid": "your-useragent-guid",
"skillkey": "cs-cron-social-content-scanner",
"enabled": true,
"interval": "0 */4 * * *"
}'
To change what the scheduled task posts (topics, tone, hashtag rules, caption style), edit the paired preference skill cs-content-tone instead — see Agent Skills → Updating Preference Skills.
Hashtags, calls to action and links come from your draft, cs-content-tone, or the agent's preferences. Without hashtag rules the agent adds 3–5 relevant hashtags; Instagram allows at most 5 per post. Instead of a drafted post, you can give the agent the caption and the images or videos in chat: the first one is the cover.
Posting a video as a Reel
Any agent with int-instagram-post and a connected account can post one finished video as a Reel, for example a video made by Video Creator. Ask for it in chat, and name the account when several are connected.
- No drafts. Instagram's API has no draft or private post, so a Reel is public as soon as it is published. The agent posts when your request asks for it, or after you approve; otherwise it waits.
- The video. A vertical (9:16) MP4 of 3 seconds to 15 minutes, up to 300 MB and at most 1920 px wide. A 16:9 video is too wide for a Reel, so Video Creator makes a 9:16 version instead.
- The caption. The hook first, then a few short lines and the call to action. Instagram doesn't make links in a caption clickable, so the call to action points to the link in your profile.
- The reply carries the Reel's link. If publishing fails, the agent checks the account's latest posts first, since the Reel may already be live.
Instagram allows each account 50 posts through the API in 24 hours.
Troubleshooting
| Error code | Meaning | What to do |
|---|---|---|
missing_params |
Callback hit without state or code. |
Start a new flow from Step 8. |
session_expired |
>15 min between OAuthConnect and callback. |
Call OAuthConnect again. |
authorization_denied |
User cancelled, or not in App Roles (Development Mode). | Add as Tester (Step 6), retry. |
token_exchange_failed |
Wrong App Secret, redirect URI mismatch, or Instagram rejected the authorization code. | Re-copy the App Secret, verify the redirect URI, and retry Instagram Login. |
useragent_not_found |
Invalid or unauthorized guid. | Use POST /UserAgent/MyAgents. |
Instagram credentials not configured |
Returned in OAuthConnect's errors[] when authmethod: "own" but appid / appsecret are missing. |
POST /UserAgent/CredentialUpsert with instagram.appid and instagram.appsecret. |
Meta could not validate this System User token |
Direct mode received an invalid token, missing permission, or inaccessible asset. | Generate a current token with the documented permissions and asset assignments, save it, and retry. |
| Valid token but no connected Instagram professional account | No assigned Page exposes a linked professional account with CREATE_CONTENT. |
Link the account to the Page, assign both assets to the System User, and reconnect. |
internal_error |
Unexpected server error. | Retry. If persistent, contact support. |
"No Instagram Business Account found" during OAuth
In System User mode, the account is still Personal, is not linked to an assigned Facebook Page, or the Page lacks CREATE_CONTENT. In advanced Instagram OAuth mode, the usual cause is a Personal account or an app-role / permission problem. Follow the mode-specific prerequisites above.
Publishing fails with "media upload failed"
Common causes:
- Image resolution too low (<320px) or aspect ratio outside Instagram's allowed ranges.
- Media that does not meet Meta's current format, duration, size, or codec requirements.
- Instagram account switched back to Personal after connection — the token becomes invalid. Ask the user to switch back to Business and reconnect.
The agent says no Instagram account is connected
The agent has int-instagram-post, but no Instagram account is connected to it. It asks you to connect one instead of posting, and scheduled posting runs do nothing. Connect an account (in System User mode, select it with SetPickerAccounts as in Step 4) and ask again.
Multi-Tenant Architecture
- One Wiro agent instance per customer.
- Recommended direct mode: each customer supplies a System User token from its own Business Portfolio and assigns its own Page plus Instagram assets.
- Advanced OAuth mode: each customer-owned Meta app controls its own App Roles, review status, and Instagram consent branding.
- Page linkage is mode-specific. It is mandatory for System User mode but not for Instagram API with Instagram Login.
- Tokens are isolated per useragent and are never returned to another customer or to the browser.