Docs › API reference › Channels
Channels API: list, connect and disconnect
List your connected channels, connect a new one with credentials or OAuth, and disconnect one.
A channel is one connected account on one platform, for example a Bluesky account. You post to channels.
Their ids go in the targets of a post.
GET /v1/channels
List channels
GET /v1/channels returns the channels that are connected now.
displayName, handle and avatarUrl can be null. providerId matches an id from
Providers.
health.state tells you whether a post to this channel is likely to work:
| State | Meaning |
|---|---|
OK |
Nothing is wrong |
DEGRADED |
The channel has a problem short of needing a reconnect. Read reason |
NEEDS_RECONNECT |
The platform no longer accepts the saved login. Posts to this channel are refused until you reconnect it |
BLOCKED |
The platform or account is blocking posts. Read reason |
reason is a sentence for a person, and action names the fix to offer. Both can be null. When a channel needs reconnecting, OmniPosts also sends the token.expired webhook.
curl https://omniposts.dev/v1/channels \
-H "Authorization: Bearer omniposts_..."
{
"items": [
{
"id": "0192a3b4-0000-7000-8000-000000000001",
"providerId": "bluesky",
"displayName": "Ada",
"handle": "ada.example.com",
"avatarUrl": null,
"health": { "state": "OK", "reason": null, "action": null },
"connectedAt": "2026-10-01T08:00:00Z"
}
]
}
DELETE /v1/channels/{id}
Disconnect a channel
DELETE /v1/channels/{id} removes the login and the cached platform data for that channel. The answer is
204. OmniPosts keeps its own history of what you posted. An id that is not yours gets 404.
This needs a signed-in person, not a key. A key gets 403 forbidden. See
Authentication. A channel.disconnected webhook is sent.
Connect a channel
Connecting is a connect session. How it goes depends on the provider's auth.kind in
Providers:
APP_PASSWORDandBOT_TOKENtake credentials that you send. The session completes at once.OAUTH2sends the person to the platform to approve. The session stayspendinguntil they return.
Starting a session is person-only (403 forbidden for a key), because a person has to approve it.
Start a session
POST /v1/connect-sessions takes:
| Field | Meaning |
|---|---|
providerId |
Required. A provider id. Missing gives 400 invalid_request |
fields |
Credentials, as a map of strings. Names come from the provider's auth.fields |
input |
Non-secret start values for OAuth, such as a server address. Names come from auth.inputs |
For a provider that takes credentials:
The answer is 201 with status: "completed" and the new channel:
For an OAuth provider the answer is 201 with status: "pending", channel: null, and an
authorizationUrl. Send the person's browser to that URL. The platform then returns them to OmniPosts (the
GET /v1/oauth/callback redirect, which is for browsers and not something you call) and OmniPosts finishes the
connection. The return must happen in the same browser session that started it.
curl https://omniposts.dev/v1/connect-sessions \
-H "Cookie: nsid=..." \
-H "X-OmniPosts-Client: web" \
-H "Content-Type: application/json" \
-d '{"providerId": "bluesky", "fields": {"identifier": "ada.example.com", "appPassword": "xxxx-xxxx-xxxx-xxxx"}}'
{
"id": "0192a3b4-0000-7000-8000-0000000000c1",
"providerId": "bluesky",
"status": "completed",
"channel": { "id": "0192a3b4-0000-7000-8000-000000000001", "providerId": "bluesky", "displayName": "Ada", "handle": "ada.example.com", "avatarUrl": null, "health": { "state": "OK", "reason": null, "action": null }, "connectedAt": "2026-10-01T08:00:00Z" },
"error": null
}
Check a session
GET /v1/connect-sessions/{id} returns the same object. Poll it after the redirect. status is one of:
| Status | Meaning |
|---|---|
pending |
Waiting for the person to approve. A pending session lasts one hour |
completed |
Connected. channel is set |
failed |
It did not work. error is one plain sentence |
expired |
The hour passed. Start again |
authorizationUrl appears only in the 201 that starts an OAuth session.
Start a connect from an API key (a handoff)
A key cannot sign in to a platform, but it can start the connect and hand a person the link. POST /v1/connect-sessions with a key (scope channels:manage) answers 201:
Give handoffUrl to a person who is a member of the workspace. When they open it, signed in, the session
becomes theirs and their browser goes to the platform; the OAuth return finishes in that same browser. The key
polls GET /v1/connect-sessions/{id} until status is completed (and channel is set), failed (error
says why: the link was opened by an account outside the workspace, the platform refused, …) or expired (an
hour after the start, or if nobody opened it).
handoffUrlis shown once, in this answer, and works once. Lose it: start again.- Platforms that connect with a pasted token or password (Telegram, an app password) cannot be handed off:
422with codehandoff_not_available. A person connects those in the app. - A session started by a person has no
handoffUrl; a session started by a key has noauthorizationUrl.
{
"id": "0192a3b4-0000-7000-8000-0000000000c1",
"providerId": "bluesky",
"status": "pending",
"handoffUrl": "https://omniposts.dev/connect/handoff/…",
"channel": null,
"error": null
}
When OmniPosts refuses
A refused start answers 422. error.code is the failure's code and details.failure is the full
failure object. Common causes:
- the provider is locked:
channel_switched_off, ornot_configuredwhen this server has no credentials for it - an unknown
providerId:unknown_provider - a credential the platform rejected
- your plan's channel limit:
plan_limit_reached, withdetails.limitset tochannels - you were removed from the workspace, or left it, while connecting:
not_a_member - the workspace was deleted while connecting:
workspace_deleted
An OAuth connect that fails after the platform's page ends with status: failed, and error holds the
same failure as one sentence, for example "You're no longer a member of this workspace, so the channel
wasn't connected."
To reconnect a channel whose state is NEEDS_RECONNECT, connect the same account again.
Related
- Reconnect a channel for the steps in the app.
- Channels for what each platform asks for.