---
title: "Connect Bluesky account"
method: POST
path: "/v1/connect/bluesky/credentials"
tags: ["Connect"]
---

# Connect Bluesky account

`POST /v1/connect/bluesky/credentials`

Connect a Bluesky account using identifier (handle or email) and an app password.
To get your userId for the state parameter, call GET /v1/users which includes a currentUserId field.

## Request body

- object
  - `identifier` string, required — Your Bluesky handle (e.g. user.bsky.social) or email address
  - `appPassword` string, required — App password generated from Bluesky Settings > App Passwords
  - `state` string, required — Required state formatted as {userId}-{profileId}. Get userId from GET /v1/users and profileId from GET /v1/profiles.
  - `redirectUri` string, uri — Optional URL to redirect to after successful connection

## Response `200`

Bluesky connected successfully

- object
  - `message` string
  - `account` SocialAccount
    - `_id` string, required
    - `platform` 'tiktok' | 'instagram' | 'facebook' | 'youtube' | 'linkedin' | 'twitter' | 'threads' | 'pinterest' | 'reddit' | 'bluesky' | 'googlebusiness' | 'telegram' | 'snapchat' | 'discord' | 'slack' | 'whatsapp' | 'linkedinads' | 'metaads' | 'pinterestads' | 'tiktokads' | 'xads' | 'googleads' | 'openaiads', required
    - `profileId` union, required
      - string
      - Profile
        - `_id` string
        - `userId` string
        - `name` string
        - `description` string
        - `color` string
        - `isDefault` boolean
        - `isOverLimit` boolean — Only present when includeOverLimit=true. Indicates if this profile exceeds the plan limit.
        - `createdAt` string, date-time
    - `username` string
    - `displayName` string
    - `profilePicture` string, nullable — URL to the account's profile picture on the platform. May be null if the platform does not provide one.
    - `profileUrl` string — Full profile URL for the connected account on its platform.
    - `isActive` boolean, required
    - `needsReconnection` boolean — The platform definitively reported the stored OAuth token as dead. While true, GET /v1/connect/{platform}/ads returns a fresh authUrl (implicit force=true) instead of alreadyConnected, so re-running the connect flow recovers the account. Cleared automatically when the account is re-authorized.
    - `followersCount` number — Follower count (only included if user has analytics add-on)
    - `followersLastUpdated` string, date-time — Last time follower count was updated (only included if user has analytics add-on)
    - `parentAccountId` string, nullable — Reference to the parent posting SocialAccount. Set for ads accounts that share or derive from a posting account's OAuth token. null for standalone ads (Google Ads) and all posting accounts.
    - `enabled` boolean — Whether the user explicitly activated this account. false means the account was created as a side effect (e.g., posting account auto-created when user connected ads first). Posting UI and scheduler ignore accounts with enabled: false.
    - `metadata` object — Platform-specific metadata. Fields vary by platform. For WhatsApp accounts, includes: - qualityRating: Phone number quality rating from Meta (GREEN, YELLOW, RED, or UNKNOWN) - nameStatus: Display name review status (APPROVED, PENDING_REVIEW, DECLINED, or NONE). A declined or pending display name does not by itself block sending; sendability is reported separately via health_status (can_send_message). - messagingLimitTier: Maximum unique business-initiated conversations per 24h rolling window (TIER_250, TIER_1K, TIER_10K, TIER_100K, or TIER_UNLIMITED). Scales automatically as quality rating improves. - verifiedName: Meta-verified business display name - displayPhoneNumber: Formatted phone number (e.g., "+1 555-123-4567") - wabaId: WhatsApp Business Account ID - phoneNumberId: Meta phone number ID For LinkedIn accounts, profileData carries the profile details refreshed on each daily snapshot: - profileData.bio: The member's headline for personal accounts, or the organization description for organization accounts. null when the member has not set one. - profileData.extraData.vanityName: The member's profile slug, i.e. the /in/{vanityName} segment of profileUrl. Personal accounts only; an organization's own slug is in metadata.organizationInfo.vanityName.

## Other responses

- `400` — Invalid request - missing fields or invalid state format
- `401` — Unauthorized
- `500` — Internal error

---

[API](https://skmtc.net/zernio/apis/zernio-api.md) · [All operations](https://skmtc.net/zernio/apis/zernio-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/zernio/zernio-api/versions/51932b099b2f/schema)
