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.

Why migrate

Buffer is a UI-first scheduling product with a long history. Its public API exists but is a secondary surface — limited in scope, with channel-by-channel publish semantics. letmepost is API-first by design: one HTTP call publishes to one or many platforms, errors are structured, idempotency is built in, and the API is the same contract that powers the dashboard. If you’re building publishing into your own product or agent and Buffer’s API has been the bottleneck, the move is a good fit. If you’re a content team that lives in Buffer’s UI, the migration story is weaker — letmepost’s dashboard is intentionally minimal.

What changes

Buffer’s model is channel-centric: you create an update against one connected channel at a time. letmepost lets you express the same idea — same content, multiple destinations — in a single request with a targets[] array.
Buffer conceptletmepost conceptNotes
ChannelPlatform accountConnect via OAuth. Each has a stable id.
UpdatePOST /v1/posts requestOne request, one or many targets.
One update per channelOne request with multiple targets[]This is the headline change. N Buffer calls collapse into 1.
Scheduled time on an updatescheduledAt (ISO-8601)Omit for immediate publish.
Channel-specific overridesper-target overrides on targets[]Platform-specific fields live on the target entry.
Error message stringEnvelope: code, rule, remediation, platformResponse, requestIdSee errors.

API shape side-by-side

Buffer’s API creates an update against a specific connected channel (“profile”). To post the same content to three channels, you make three calls.
# One call per channel — repeat for each profile_id you want to publish to.
curl -X POST https://api.bufferapp.com/1/updates/create.json \
  -H "Authorization: Bearer $BUFFER_ACCESS_TOKEN" \
  --data-urlencode "profile_ids[]=<profile_id_1>" \
  --data-urlencode "text=Hello" \
  --data-urlencode "scheduled_at=2026-05-20T12:00:00Z"
Buffer’s profile_ids[] parameter does accept multiple ids in a single create call, so a fan-out is possible there too. The bigger difference is what comes back: letmepost returns one consolidated response with per-target outcomes, the same requestId traces the whole pipeline (preflight, publish, webhook), and any failure is the same envelope shape regardless of which platform rejected it.

Cutover playbook

1

Sign up and connect your accounts

  • Create an account at dashboard.letmepost.dev, or self-host.
  • Mint an API key (Settings → API keys).
  • Connect each platform you currently publish to from Buffer. letmepost uses each platform’s OAuth flow — re-authorize the same accounts against the letmepost app.
  • Copy each connected account’s id from the dashboard — these become your targets[].accountId values.
2

Collapse N calls into 1

If your current Buffer integration loops over channels and calls updates/create.json per channel, that loop goes away. Build a single request body with one entry in targets[] per channel and send once. Same content, one network round-trip, one response to reconcile.
3

Map your existing payload

  • Rename texttext (same).
  • Rename scheduled_atscheduledAt. Include the Z or an explicit offset; naive datetimes are rejected.
  • Translate channel-specific overrides into per-target fields. See platforms for the field set per platform.
  • For media, prefer POST /v1/media once, then reference by mediaId on every post. URL passthrough and inline base64 are also supported.
  • Run a few candidate posts through preflight — letmepost catches platform-specific issues (grapheme caps, mime allow-lists, media counts) before the upstream call.
4

Add the idempotency header

letmepost dedupes retries via Idempotency-Key — generate one UUID per logical post and replay safely on network errors. The replay cache returns the original response (same status, same body) for 24 hours. Reusing the same key with a different body returns idempotency_conflict, which catches accidental drift in your client. Buffer does not document a request-level idempotency contract, so anything you’d been doing in your own code to dedupe retries can move to the header.
5

Handle errors with the new envelope

Every letmepost failure carries code, message, rule, remediation, platformResponse, platformVersion, and requestId. Branch on code and rule, not message text. The raw upstream response is attached when the platform rejected the call, so you keep the original signal instead of a flattened message string. See errors for the eleven codes and preflight for the rule catalog.
6

Pin platform versions explicitly

letmepost pins each upstream platform to a known API version (e.g. Linkedin-Version for LinkedIn). When a platform deprecates the version we’re on, you get a version.deprecated webhook with a window to migrate — your live traffic doesn’t break silently. This is the kind of platform-churn buffering that Buffer’s API doesn’t expose to callers.
7

Verify with one parallel post

  • For about a week, publish each post through both Buffer and letmepost.
  • Reconcile: same content lands on the same platforms, with the same timestamps and the same media.
  • Once you’re confident, switch traffic over.
8

Decide what to keep

Many teams will keep Buffer for the human scheduling workflow and use letmepost for programmatic publishing — they don’t conflict. If Buffer’s UI was the primary value, keep it. If the API was, the move is clean.

Feature parity

CapabilityBufferletmepost.dev
Hosted publishing API
Open source✓ (Apache-2.0)
Self-hostable✓ (guide)
Multi-target post in one request✓ (profile_ids[])✓ (targets[])
Scheduled posts
Preflight catalog with stable rule ids✓ (preflight)
Structured error envelope (raw platformResponse attached)✓ (errors)
Request-level idempotency (Idempotency-Key, body-hash conflict)✓ (idempotency)
Pinned platform versions, version.deprecated webhook
Webhooks for post lifecyclelimited✓ (webhooks)
Scheduling / queue UI for end users✓ (mature)minimal
Analytics dashboardsnot in v1
Engagement / reply managementnot in v1
AI assistant / draftingnot in scope
Official SDKs (TypeScript, Python, Go)✓ (SDKs)
CLI + MCP server✓ (CLI, MCP)
Buffer is a mature scheduling product with strong UI workflows and analytics. letmepost is a publishing API with a minimal dashboard. If you’re building publishing into your own product, the migration is straightforward. If your team works inside Buffer’s UI, plan to keep using it for that. See pricing for the current letmepost plan.

Need help

Open an issue on GitHub or email rose@letmepost.dev. If you’re migrating from Buffer, we’ll prioritize debugging help.