Connections API
A connection is one social account linked to a workspace. Posts target connections by slug, such as instagram-mybrand. This page covers listing connections, checking their health, refreshing tokens, getting a reconnect link, and how the connect flows work.
Connection routes live under /api/workspaces/:id/connections and /api/oauth. With an API key the workspace comes from the key. With a dashboard session token send X-Workspace-Id. See the REST API overview.
Which connection endpoints can I call?
| Method | Path | Purpose | Permission | Guarded API key |
|---|---|---|---|---|
GET | /workspaces/:id/connections | List connections with status | platform.connect | Yes |
POST | /workspaces/:id/connections/:connectionId/refresh | Refresh tokens (slug or ID) | platform.connect | No |
POST | /oauth/connections/:ref/reconnect-link | Get a re-authorize link | Workspace owner | No |
POST | /oauth/:platform/popup-code | Mint a code to open the connect window | Workspace owner, session token | No |
GET | /oauth/:platform/connect-popup?code= | Start the connect flow in a browser | Single-use code | n/a |
PATCH | /workspaces/:id/connections/:connectionId/target | Switch the page or account a connection posts to | platform.connect | No |
POST | /workspaces/:id/connections/:connectionId/fork | Add a second page from the same login | platform.connect | No |
DELETE | /workspaces/:id/connections/:connectionId | Disconnect | platform.disconnect | No |
DELETE | /workspaces/:id/connections/:connectionId/revoke | Revoke and delete | platform.disconnect | No |
The :connectionId in the last four rows is a connection ID. The refresh route accepts a slug or an ID. The platform.connect permission belongs to the Owner role, so Admin, Editor and Contributor sessions get 403 on list and refresh. An API key acts as the workspace owner.
How do I list connected accounts and their status?
curl https://api.socifyr.com/api/workspaces/6a04f0000000000000000001/connections \
-H "Authorization: Bearer sk_..."{
"success": true,
"data": [
{
"id": "6a04f715d3d7d93575e65d13",
"slug": "instagram-mybrand",
"platform": "instagram",
"platformUserId": "17841400000000000",
"platformUsername": "mybrand",
"profileImageUrl": "https://cdn.example.com/mybrand.jpg",
"targetId": "17841400000000000",
"targetName": "@mybrand",
"targetImageUrl": "https://cdn.example.com/mybrand.jpg",
"connectedAt": "2026-10-01T10:00:00.000Z",
"expiresAt": "2026-12-20T10:00:00.000Z",
"lastRefreshedAt": "2026-11-10T04:00:00.000Z",
"hasRefreshToken": false,
"status": "active",
"canRefresh": true
}
]
}Other fields: disconnectedAt (set on disconnected accounts), and availableTargets when a login has several pages to choose from.
What do the status values mean?
status | Meaning | What to do |
|---|---|---|
active | Nothing to do. This includes tokens near expiry that renew themselves. | Post normally. |
expiring | The token expires within 7 days and cannot renew itself, or it has already lapsed but can renew. | If canRefresh is true, call refresh. Otherwise plan a reconnect. |
needs_reconnect | The account is disconnected, or the token lapsed and cannot renew. | Get a reconnect link and have the account owner approve it. |
canRefresh is true when the platform supports token refresh. The background expiry cron also refreshes tokens before they lapse. Concepts and platform differences are in Connections and slugs.
How do I refresh a connection token?
POST /workspaces/:id/connections/:connectionId/refresh, no body. The last path segment can be the slug.
curl -X POST https://api.socifyr.com/api/workspaces/6a04f0000000000000000001/connections/instagram-mybrand/refresh \
-H "Authorization: Bearer sk_..."{
"success": true,
"data": {
"id": "6a04f715d3d7d93575e65d13",
"slug": "instagram-mybrand",
"platform": "instagram",
"expiresAt": "2027-01-12T10:00:00.000Z",
"lastRefreshedAt": "2026-11-13T09:00:00.000Z",
"status": "active",
"canRefresh": true
}
}| Status | Cause | Fix |
|---|---|---|
| 400 | Token refresh is not supported for <platform>. Please reconnect instead. | Use the reconnect link below. |
| 404 | Connection not found | Check the slug and workspace. |
| 403 | Guarded API key | Use an unrestricted key or a dashboard session. |
How do I get a reconnect link for an expired account?
POST /oauth/connections/:ref/reconnect-link, where :ref is the connection slug or ID. No body. Only the workspace owner can use it, which an unrestricted API key does as the owner. Guarded keys are refused.
curl -X POST https://api.socifyr.com/api/oauth/connections/instagram-mybrand/reconnect-link \
-H "Authorization: Bearer sk_..."{
"success": true,
"data": {
"url": "https://api.socifyr.com/api/oauth/instagram/connect-popup?code=3f9c...",
"expiresIn": 600,
"platform": "instagram",
"slug": "instagram-mybrand"
}
}Give url to the account owner. It is single use and valid for expiresIn seconds (600). It carries a one-time code, not an access token. When the owner approves the same account, the existing connection and its slug update in place, so scheduled posts keep working. Approving a different account creates a new connection. After they approve, retry any failed post with POST /content/:id/retry.
| Status | Cause |
|---|---|
| 403 | Only the workspace owner can connect accounts |
| 404 | Connection not found |
Reconnect also works for connections that are already disconnected. For the step-by-step recovery flow see Connections and slugs.
Can I connect a new account through the API?
Not with an API key alone. A new connection needs a person in a browser.
- A signed-in dashboard session calls
POST /oauth/:platform/popup-codeand receives{ "code": "...", "expiresIn": 60 }. Only the workspace owner can mint it. - The browser opens
GET /oauth/:platform/connect-popup?code=<code>. The code works once. - For OAuth platforms, the browser is redirected to the platform's consent screen and back to Socifyr's callback.
- For credential-entry platforms, Socifyr shows a form instead. The form is submitted with a short-lived, single-use session cookie that only the popup holds, so you cannot script it with
curl.
Use the dashboard or a reconnect link for connecting. The CLI, SDK and MCP can list, refresh and reconnect, but not create the first connection.
Which platforms use a credential form?
| Platform | Fields |
|---|---|
| Bluesky | identifier (handle), appPassword |
| Telegram | botToken, chatId |
| Discord | webhookUrl |
| Mastodon | instanceUrl, accessToken |
| Nostr | privateKey, optional comma-separated relays |
| Slack | webhookUrl |
| Dev.to | apiKey |
| Hashnode | token |
| Tumblr | consumerKey, consumerSecret, oauthToken, oauthTokenSecret |
All other supported platforms use OAuth. See each page under Platforms for the exact steps and permissions.
How do I disconnect an account?
| Route | Effect |
|---|---|
DELETE /workspaces/:id/connections/:connectionId | Revokes the platform token where the platform allows it and marks the connection disconnected. The record stays. |
DELETE /workspaces/:id/connections/:connectionId/revoke | Revokes the token and deletes the record. |
Both return 204 and need the connection's ID. Both need platform.disconnect, which Admins do not have by default. Only Owners do. See Workspaces API for roles.
How do I switch the page an account posts to?
For Facebook and Instagram logins that manage several pages:
curl -X PATCH https://api.socifyr.com/api/workspaces/6a04f0000000000000000001/connections/6a04f715d3d7d93575e65d13/target \
-H "Authorization: Bearer <session-token>" \
-H "X-Workspace-Id: 6a04f0000000000000000001" \
-H "Content-Type: application/json" \
-d '{ "targetId": "17841400000000001", "targetName": "@mybrand.shop" }'POST .../fork with { "targetId", "targetName", "targetImageUrl" } creates a second connection for another page using the same login. The target routes do not validate a body schema beyond these fields, so send exactly these names.
Frequently asked questions
Why does a post fail with reconnect_required? The account's token expired and cannot renew. Get a reconnect link, have the owner approve it, then retry.
Do slugs change when I reconnect? Not when you approve the same account. The slug stays the same.
Can a guarded API key list connections? Yes. Listing is allowed, so an agent can find slugs. Refreshing and reconnecting are not.