Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.letmepost.dev/llms.txt

Use this file to discover all available pages before exploring further.

In 90 seconds you’ll publish to Bluesky from your terminal. Bluesky is the only platform that’s live for everyone today — the others are gated by platform review and ship as approvals clear.

1. Get an API key

Sign in at letmepost.dev/dashboard and mint a key from the API keys page. Keys look like lmp_live_… (production) or lmp_test_… (sandbox). Treat them like passwords; the dashboard only shows the secret once.

2. Connect a Bluesky account

Bluesky is the only platform that uses an app password instead of OAuth. Generate one at bsky.app/settings/app-passwords — copy the 19-character password it shows you. Then submit it through the dashboard’s Accounts page (or via the API):
connect-bluesky.sh
# Step 1 — start the connect flow.
curl -X POST https://api.letmepost.dev/v1/accounts/connect/bluesky \
  -H "Authorization: Bearer $LMP_KEY"

# Step 2 — submit the handle + app password.
curl -X POST https://api.letmepost.dev/v1/accounts/connect/bluesky/complete \
  -H "Authorization: Bearer $LMP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "handle": "you.bsky.social",
    "appPassword": "abcd-efgh-ijkl-mnop"
  }'
The complete response carries the account id you’ll send posts against. Save it — that’s the value for account.id below.

3. Send a post

curl -X POST https://api.letmepost.dev/v1/posts \
  -H "Authorization: Bearer $LMP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "account": {
      "platform": "bluesky",
      "id": "00000000-0000-0000-0000-000000000000"
    },
    "text": "Hello from letmepost.dev"
  }'
Replace the id with the account id you got back from the connect flow. The active language tab is sticky — picking TypeScript here will follow you across every code sample on this site.

4. Read the response

A successful immediate publish returns 201 Created:
201.json
{
  "id": "post_01HY6X4AWBJM2K9F2PTQMRD9JQ",
  "platform": "bluesky",
  "status": "published",
  "uri": "at://did:plc:abc/app.bsky.feed.post/3kr...",
  "cid": "bafyrei...",
  "createdAt": "2026-05-04T12:00:00.000Z"
}
The uri is Bluesky-native and resolves to the post on bsky.app. Save the id — that’s how you’ll fetch the record later via GET /v1/posts/:id.

5. What happens when it fails

Send a 400-character post and you’ll get this back instead:
400.json
{
  "error": {
    "code": "preflight_failed",
    "rule": "bluesky.text.max_graphemes",
    "platform": "bluesky",
    "message": "Post text is 400 graphemes; Bluesky allows at most 300.",
    "remediation": "Shorten the post to 300 graphemes or fewer.",
    "requestId": "req_01HY6X4AWBJM2K9F2PTQMRD9JQ"
  }
}
Notice the rule id (bluesky.text.max_graphemes) — that’s the canonical link to the preflight rule page explaining what it checks and where the constraint comes from. This is the contract for every error. Stable code, rule that fired, remediation, request id you can grep. Always. See errors for the full catalog.

Next

Idempotency

Why every retry is safe.

Errors

The eleven codes.

Bluesky platform

Bluesky’s payload, scope set, and rule list.

API reference

POST /v1/posts parameter by parameter.