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.

Updated

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_PASSWORD and BOT_TOKEN take credentials that you send. The session completes at once.
  • OAUTH2 sends the person to the platform to approve. The session stays pending until 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).

  • handoffUrl is 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: 422 with code handoff_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 no authorizationUrl.
{
  "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, or not_configured when 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, with details.limit set to channels
  • 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.