Connect Credential Platforms
Connect Bluesky and other credential-based platforms
Supported Platforms
- Bluesky - App password-based (currently available)
- Additional platforms coming soon
Bluesky Connection
Bluesky uses app passwords for third-party authentication. Unlike OAuth, this is a direct credential exchange where users generate a password specifically for your application.
Understanding App Passwords
App passwords are Bluesky's way of allowing third-party apps to access accounts without sharing the main password. They:
- Are generated in Bluesky settings, not by the user
- Follow the format
xxxx-xxxx-xxxx-xxxx(16 characters with hyphens) - Never expire unless manually revoked by the user
- Can be named to help users remember which app they're for
- Can be revoked individually without affecting the main account
User Flow
When a user wants to connect their Bluesky account to your app:
-
Direct them to Bluesky settings: Send users to bsky.app/settings/app-passwords
-
They create an app password:
- Click "Add App Password"
- Name it (e.g., "YourAppName" or "My Social Scheduler")
- Copy the generated password immediately (it won't be shown again)
-
They provide credentials to your app: User enters both their handle and the app password in your interface
-
Your app connects via postcore: Send the credentials to postcore's API
-
Connection complete: The account is now connected and ready for posting
Implementation
Endpoint:
POST /profiles/connectRequest:
const response = await fetch('https://api.postcore.dev/profiles/connect', {
method: 'POST',
headers: {
'x-api-key': process.env.POSTCORE_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
profileKey: 'prof_abc123', // Or omit to auto-create
platform: 'bluesky',
credentials: {
handle: 'user.bsky.social',
appPassword: 'abcd-efgh-ijkl-mnop'
}
}),
});
const data = await response.json();
console.log('Connected:', data.username); response = requests.post(
'https://api.postcore.dev/profiles/connect',
headers={
'x-api-key': os.getenv('POSTCORE_API_KEY'),
'Content-Type': 'application/json'
},
json={
'profileKey': 'prof_abc123', # Or omit to auto-create
'platform': 'bluesky',
'credentials': {
'handle': 'user.bsky.social',
'appPassword': 'abcd-efgh-ijkl-mnop'
}
}
)
data = response.json()
print(f"Connected: {data['username']}") curl -X POST https://api.postcore.dev/profiles/connect \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"profileKey": "prof_abc123",
"platform": "bluesky",
"credentials": {
"handle": "user.bsky.social",
"appPassword": "abcd-efgh-ijkl-mnop"
}
}'Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
profileKey | string | No* | Profile to connect to (*auto-created if omitted) |
platform | string | Yes | Must be "bluesky" |
credentials.handle | string | Yes | Bluesky handle (e.g., user.bsky.social) |
credentials.appPassword | string | Yes | App password from Bluesky settings |
Response:
{
"profileKey": "prof_abc123",
"platform": "bluesky",
"username": "user.bsky.social"
}Status Code: 201 Created
Important Notes
Handle Format: The handle must include the full domain (e.g., user.bsky.social). Common custom domains work too (e.g., user.customdomain.com).
Storage: postcore stores app passwords encrypted. Your application never needs to store the app password - just save the profileKey returned from the connection.
Auto-Creating Profiles: If you omit the profileKey parameter, postcore will automatically create a new profile and return its profileKey in the response. Save this with your user's account.
Expiry: App passwords never expire unless the user manually revokes them in their Bluesky settings. Monitor the needsReconnect field in the profile data to detect if a password has been revoked.
UI Recommendations
When building your connection interface:
-
Provide clear instructions: Link directly to bsky.app/settings/app-passwords and explain the steps
-
Show the expected format: Display placeholder text
xxxx-xxxx-xxxx-xxxxin the password field -
Explain why: Tell users this is for security - it's not their main password and can be revoked anytime
-
Name suggestion: Recommend they name it after your app so they remember what it's for
-
Handle validation: Check that the handle includes a domain (
.bsky.socialor custom domain)
Error Responses
Invalid Credentials
{
"error": "INVALID_CREDENTIALS",
"message": "Invalid Bluesky credentials"
}Status: 401 Unauthorized
Common causes:
- Wrong handle format (missing
.bsky.socialor domain) - App password was revoked or doesn't exist
- Typo in handle or password
- Password copied incorrectly (includes spaces or wrong characters)
Profile Not Found
{
"error": "PROFILE_NOT_FOUND",
"message": "Profile not found"
}Status: 404 Not Found
Solution: The specified profileKey doesn't exist. Either create the profile first or omit profileKey to auto-create.
Wrong Method for OAuth
{
"error": "USE_GET_FOR_OAUTH",
"message": "linkedin requires OAuth. Use GET /profiles/connect?platform=linkedin&apiKey=YOUR_KEY&returnUrl=YOUR_URL"
}Status: 400 Bad Request
Solution: This endpoint is for credential-based platforms only. For OAuth platforms like LinkedIn, see Connect OAuth Platforms.