Accounts and profiles
Connect Instagram accounts through OAuth, group them per client with profiles, monitor the health of each connection and disconnect.
A connected account (integration) is an Instagram account that authorized Postmatic to publish on its behalf. A profile is an optional label to group accounts, for instance one client of your agency or one brand.
Account requirements
- A professional Instagram account: Business or Creator. A personal account is rejected during authorization with
ACCOUNT_NOT_BUSINESS; the user switches the account type in Instagram's settings and tries again. - The connection uses Meta's Instagram Login. No linked Facebook Page is required.
- Permissions granted: basic profile, content publishing, metrics, comments and direct messages.
The connection asks for five permissions: publishing, basic reading, metrics, comments and messages. An account connected before metrics, comments or messages existed needs to be reconnected to use them; scopes in the listing shows what each account granted.
Connecting through the API
The flow has three steps: request the URL, redirect the user, receive the return.
1. Request the authorization URL
curl "https://api.uat.postmatic.dev/v1/connect/instagram?redirectUri=https://app.example.com/accounts&profileId=prf_…" \
-H "x-access-key: pm_live_…"| Parameter | Required | Description |
|---|---|---|
redirectUri | no | Where the user lands when done. http or https only. Without it, the return is a JSON with the connected account (handy for testing). |
profileId | no | Profile the account will belong to. Must exist in the project. |
appId | no | Accepted and ignored. Connections always use Postmatic's app. |
{ "url": "https://www.instagram.com/oauth/authorize?…" }The URL is valid for 10 minutes. Only the instagram platform is available; any other answers 403 PLATFORM_NOT_AVAILABLE.
2. Redirect the user
Open the url in the user's browser. They log in to Instagram and authorize. Do not open it in an iframe: Meta blocks it.
3. Receive the return
Meta sends the user back to Postmatic, which exchanges the code for a long-lived token, stores it encrypted and redirects to your redirectUri with the result in the query string.
| Outcome | Query string |
|---|---|
| Success | ?status=connected&integrationId=int_… |
| Failure | ?status=error&code=ACCOUNT_NOT_BUSINESS (or another code from Errors) |
Keep the integrationId: it is the accountId of your posts. If the same Instagram account is connected again in the same project, Postmatic updates the existing connection instead of creating another. That is how you reconnect an account whose token expired.
Listing accounts
curl "https://api.uat.postmatic.dev/v1/connect/integrations?profileId=prf_…&limit=50" \
-H "x-access-key: pm_live_…"| Parameter | Default | Description |
|---|---|---|
platform | Filter by platform (instagram). | |
profileId | Filter by profile. The literal string "null" returns accounts without a profile. | |
q | Search by part of the username. | |
limit | 20 | 1 to 100. |
offset | 0 | How many to skip. |
sort | desc | asc or desc, by connection date. |
{
"success": true,
"total": 1,
"integrations": [
{
"id": "int_…",
"platform": "instagram",
"platformUserId": "17841400000000000",
"displayName": "acme.brand",
"imageUrl": "https://…",
"profileId": "prf_…",
"createdAt": "2026-09-01T12:00:00.000Z",
"authStatus": "active",
"status": "connected",
"tokenExpiresAt": "2026-10-31T12:00:00.000Z",
"lastHealthCheckAt": "2026-09-08T06:30:00.000Z",
"healthError": null
}
]
}The first seven fields are the basic account contract. The rest is the connection health, which Postmatic maintains on its own:
| Field | Values | Use |
|---|---|---|
authStatus | active, reconnect_required | The one field to check before publishing. reconnect_required means an expired token or a revoked account: send the user to connect again. |
status | connected, degraded, revoked | Result of the last check. degraded is a temporary Meta failure; revoked is final. |
tokenExpiresAt | date | When the long-lived token expires. |
healthError | text or null | CODE: message of the last failure. |
Health of one account
To check an account right now instead of waiting for the routine:
curl "https://api.uat.postmatic.dev/v1/integrations/int_…/health?force=true" \
-H "x-access-key: pm_live_…"{
"success": true,
"health": {
"integrationId": "int_…",
"status": "connected",
"authStatus": "active",
"healthy": true,
"tokenExpiresAt": "2026-10-31T12:00:00.000Z",
"tokenRefreshedAt": "2026-09-01T12:00:00.000Z",
"daysUntilExpiry": 53,
"lastHealthCheckAt": "2026-09-08T14:02:11.000Z",
"healthError": null,
"scopes": ["instagram_business_basic", "instagram_business_content_publish"],
"checkedLive": true
}
}Postmatic only asks Meta if the last check is older than 10 minutes; before that it returns the stored result with checkedLive: false. force=true always asks. Every live check counts against the account's Meta quota, so do not call it in a loop.
Tokens and renewal
Meta's token lasts 60 days. Postmatic renews, every 6 hours, tokens older than 7 days since their last renewal, and checks the health of every account at the same cadence. You only need to act when authStatus becomes reconnect_required: that happens when the user changes their password, removes the app on Instagram or the account gets restricted.
Disconnecting
curl -X DELETE https://api.uat.postmatic.dev/v1/connect/integrations/int_… \
-H "x-access-key: pm_live_…"The token is erased and the account leaves the listing. Posts already published stay on Instagram; scheduled posts for that account will fail with TOKEN_INVALID.
Profiles
Use profiles when you serve several clients from one project: create one per client, pass the profileId when connecting and filter accounts and posts by it.
curl -X POST https://api.uat.postmatic.dev/v1/profiles \
-H "x-access-key: pm_live_…" \
-H "content-type: application/json" \
-d '{ "name": "Acme", "description": "Client since 2026" }'| Route | What it does |
|---|---|
POST /v1/profiles | Creates. name up to 120 characters, optional description up to 500. Answers 201. |
GET /v1/profiles | Lists all, newest first. |
DELETE /v1/profiles/:id | Removes. Linked accounts keep existing and keep their profileId. |
In account and post listings, profileId="null" (the string) selects what has no profile.