Skip to main content
A “platform account” is a single connected identity on an upstream platform — one Bluesky handle, one Facebook Page, one Twitter user. Posts are sent against an account id, never against bare credentials.

Two connect shapes

platformshapeflow
Blueskycredentialssubmit handle + app password, no OAuth round-trip
everything elseoauthredirect → upstream consent → callback
Both end with a row in platform_accounts and an id you reference from post bodies as targets[].accountId.

OAuth: connect → complete

connect.sh
# 1. Start the flow.
curl -X POST https://api.letmepost.dev/v1/accounts/connect/linkedin \\
  -H "Authorization: Bearer $LMP_KEY"

# Response: { "redirectUrl": "https://www.linkedin.com/oauth/...", "state": "..." }

# 2. Send your user to redirectUrl. After they consent, LinkedIn
#    redirects to https://api.letmepost.dev/v1/accounts/connect/linkedin/complete
#    with a ?code= and ?state= the API exchanges for tokens.
The dashboard’s Accounts page wraps both ends of this for you. Programmatically, you initiate the flow yourself and your user comes back to the API’s complete endpoint.

Credentials: Bluesky

Bluesky doesn’t have OAuth; it uses scoped app passwords. Generate one at bsky.app/settings/app-passwords, then submit:
bluesky.sh
curl -X POST https://api.letmepost.dev/v1/accounts/connect/bluesky/complete \\
  -H "Authorization: Bearer $LMP_KEY" \\
  -H "Content-Type: application/json" \\
  -d '{ "identifier": "you.bsky.social", "appPassword": "abcd-efgh-ijkl-mnop" }'
We never store your main account password. Revoke an app password at any time on bsky.app and the connection breaks cleanly with platform_auth_failed.

Meta surfaces each connect separately

Facebook Pages, Instagram Business, and Threads each have their own connect flow — one OAuth per platform_accounts row. Connecting facebook discovers Pages via GET /me/accounts; connecting instagram runs the standalone Instagram Login OAuth and uses /me?fields=id for the Content Publishing API user id; connecting threads runs Threads’ own OAuth at threads.net. No silent fan-out — every connected identity is one explicit OAuth.

Token lifecycle

We refresh access tokens on each platform’s published schedule and AES-256-GCM encrypt them at rest:
platformaccess token lifetimestrategy
Blueskyminutesrefresh on 401; the AT Proto session is short-lived
Twitter / X2 hoursrefresh ~5 min before expiry
Threads~60 daysrefresh long-lived token before expiry
Facebook / IG~60 daysexchange short-lived for long-lived; refresh on schedule
LinkedIn60 days, no refreshre-auth required; we fire token.expiring 7 days ahead
Pinterest30 daysrefresh ~24 h before expiry
Subscribe to token.expiring and token.revoked to know when a connection needs user attention.

Listing and disconnecting

GET    /v1/accounts                — list connected accounts (no secrets)
GET    /v1/accounts/:id            — single account detail
DELETE /v1/accounts/:id            — disconnect (hard delete)
Secrets never come back from a read. Only the public profile (display name, platform user id), the token expiry timestamp, and platform-specific public extensions (Pinterest’s default board, etc.) are exposed.

Errors during connect

codewhen
platform_rejectedUpstream OAuth flow failed (consent denied, bad scopes, missing business asset).
platform_auth_failedA subsequent post hit a revoked or expired token.
facebook.pages.noneThe Facebook user has no Pages they can manage; can’t connect.

See also