Buffer CLI Playbook
Procedures, command workflows, and safety gates for scheduling social media posts, managing channels, and automating publication workflows via the Buffer CLI (@bufferapp/cli).
Architecture & Progressive Disclosure
To minimize context consumption, SKILL.md contains core operational commands, critical pitfalls, and safety rules directly in the body. Load specialized references on demand:
- Automation Workflows: Read references/workflows.md for shell scripting patterns, timezone math, and Relay cursor pagination.
- Rate Limits & Idempotency: Read references/rate_limits.md for 429 backoff algorithms, retry matrices, and duplicate-post prevention.
1. Bootstrapping & Installation
The Buffer CLI is generated from Buffer's public GraphQL schema, returning structured JSON with predictable error handling.
Agent Bootstrap Sequence
When running in a new environment or container, follow this self-bootstrapping sequence:
# 1. Check if the Buffer CLI is already installed
if ! command -v buffer &> /dev/null; then
echo "Buffer CLI not found. Installing globally via npm (requires Node.js 18+)..."
npm install -g @bufferapp/cli
fi
# 2. Verify installation version
buffer --version
# 3. Diagnose environment, config, API token, and network reachability
buffer doctor
[!TIP] In ephemeral sandbox environments where global npm installation is restricted, you can invoke the CLI on the fly using
npx:npx -y @bufferapp/cli doctor
Authentication Modes
- Environment Variable (Recommended for CI / Ephemeral Agents):
export BUFFER_API_KEY="your-api-key" - Global Configuration (
buffer init):
Writes API token, default organization, and timezone tobuffer init$XDG_CONFIG_HOME/buffer/config.json(or~/.config/buffer/config.json).
2. Core Operational Workflows
[!IMPORTANT] Always use
--output jsonwhen invoking commands within automated scripts or agent subshells to ensure clean machine parsing.
Workflow A: Channel Discovery & Account Inspection
Always inspect available channels before dispatching posts to resolve target channelIds:
# Inspect account details and default organization
buffer account --output json
# List all connected social channels (LinkedIn, X, Bluesky, Threads, Instagram, etc.)
buffer channels list --output json
# Get detailed metadata for a specific channel
buffer channels get --id "<channel-id>" --output json
Workflow B: Safe Post Creation & Scheduling
Always execute with --dry-run first to validate the payload structure before sending live mutations:
# Step 1: Dry run validation
buffer posts create \
--channel-id "<channel-id>" \
--scheduling-type automatic \
--mode addToQueue \
--text "Your post content here" \
--dry-run
# Step 2: Live creation (Add to channel queue)
buffer posts create \
--channel-id "<channel-id>" \
--scheduling-type automatic \
--mode addToQueue \
--text "Your post content here" \
--output json
Media Hosting Requirement (No File Upload Attachments)
[!WARNING] Buffer Does Not Support Direct File Uploads / Attachments: Buffer's API and CLI do not accept raw binary file uploads or local file paths (such as
path/to/image.png). To add media (images or videos) to a post, the files MUST be hosted somewhere publicly accessible via HTTP/HTTPS (e.g. S3 bucket, Cloud Storage, GitHub raw content, CDN, or image hosting service) so Buffer can ingest them via their public URLs.
Pass hosted media URLs via the assets array in a JSON payload:
{
"channelId": "channel_123",
"schedulingType": "automatic",
"mode": "addToQueue",
"text": "Announcing our new open-source release! 🚀",
"assets": [
{
"image": {
"url": "https://cdn.example.com/images/architecture-diagram.png",
"metadata": {
"altText": "Architecture diagram illustrating worker pool"
}
}
}
]
}
For videos, provide the public video URL:
{
"channelId": "channel_123",
"schedulingType": "automatic",
"mode": "addToQueue",
"text": "Watch the terminal recording in action:",
"assets": [
{
"video": {
"url": "https://cdn.example.com/videos/demo.mp4"
}
}
]
}
Passing Payloads via JSON or File
For complex multi-line text, media attachments, or structured objects:
# Inline JSON payload with hosted media
buffer posts create --json '{
"channelId": "channel_123",
"schedulingType": "automatic",
"mode": "addToQueue",
"text": "Line 1\n\nLine 2 with links",
"assets": [
{
"image": {
"url": "https://cdn.example.com/diagram.png",
"metadata": { "altText": "Architecture diagram" }
}
}
]
}' --output json
# Read payload from file
buffer posts create --input post_payload.json --output json
# Pipe payload from stdin
cat post_payload.json | buffer posts create --input - --output json
Workflow C: Drafting Ideas
Create draft thoughts and ideas in Buffer without assigning them immediately to a channel queue:
# Create an idea in an organization
buffer ideas create \
--organization-id "<org-id>" \
--text "Draft angle for next week's release" \
--output json
# Create an idea with structured JSON
buffer ideas create --json '{
"organizationId": "org_123",
"content": { "text": "Architectural breakdown draft" }
}' --output json
Workflow D: Inspecting & Monitoring Scheduled Posts
# List recent posts on a channel
buffer posts list --channel-id "<channel-id>" --output json
# Fetch specific post status
buffer posts get --id "<post-id>" --output json
3. Field Selection (--fields)
To minimize payload sizes and optimize context tokens, filter responses using comma-separated dot-notation paths or brace expansion:
# Select top-level and nested properties
buffer posts get --id "<post-id>" --fields id,text,channel.name --output json
# Brace expansion for list connections
buffer posts list --channel-id "<channel-id>" --fields 'items.{id,text,status},pageInfo.endCursor' --output json
# Retrieve complete GraphQL payload
buffer posts get --id "<post-id>" --fields all --output json
4. Dynamic Schema Introspection
When crafting payloads with unknown parameters or enums, query the live schema directly:
# List all available command groups
buffer schema list
# Inspect exact input types, enum values, and output shapes for a command
buffer schema describe posts create
5. Global Flags & Exit Codes
Global Flags
| Flag | Description | Best Practice |
|---|---|---|
--output <json|pretty|auto> |
Output renderer format | Always specify --output json in agent tooling |
--dry-run |
Validates input locally without network calls | Always run before stateful mutations |
--quiet |
Suppress spinners and stderr notices | Recommended for headless execution |
--verbose |
Print rate-limit summary after requests | Useful for debugging throughput limits |
--timeout <ms> |
Command timeout in milliseconds (default: 30000) | Set appropriately for large batch requests |
Exit Code Reference
| Exit Code | Classification | Cause & Agent Remediation |
|---|---|---|
0 |
Success | Command completed successfully. |
1 |
General Error | Runtime failure. Check error message on stderr. |
2 |
Usage / Validation Error | Missing required flags, invalid JSON, or schema mismatch. Run buffer schema describe <group> <cmd>. |
3 |
API Error | GraphQL upstream error or rate limit exhaustion. Inspect returned error details. |
4 |
Authentication Error | Missing or invalid token. Run buffer doctor or export BUFFER_API_KEY. |
6. Critical Pitfalls & Payload Rules
Review these high-risk failure modes before composing commands and JSON payloads:
1. Media Hosting & File Attachments
- Buffer Does Not Support Local Attachments: You cannot upload local file paths. All media (
assets[].image.urlorassets[].video.url) must be hosted publicly via HTTP/HTTPS (e.g. S3, GCS, CDN, or raw GitHub link). - Empty
textwithout assets is rejected: Most channels require text or at least one image/video asset.
2. Scheduling Modes & Notification Traps
mode: addToQueueis queued, not immediate: Usemode: shareNowto publish immediately.mode: shareNextjumps to the front of the queue.mode: customScheduledrequiresdueAt.schedulingType: notificationdoes not auto-publish: It only sends a push notification to the user's mobile app. Always useschedulingType: automaticfor hands-off publishing.addToQueueon a channel with no schedule: Silently lands in an empty queue slot without scheduling. Check the schedule withbuffer channels get --id <id>first.- All times must be ISO-8601 with offset:
dueAtrequires a timezone offset (e.g.2026-05-06T17:00:00-05:00). Obtain the offset frombuffer config get timezoneorbuffer account --fields timezone. Never assume UTC.
3. Identifier Integrity
- Never guess channel IDs: Always fetch with
buffer channels list --output json. Invalid IDs will be accepted initially and fail on execution with vague upstream errors. - IDs are not portable across organizations: A
channelIdfrom Organization A cannot be used while authenticated against Organization B.
4. Input & Formatting Constraints
--jsonoverrides flags entirely: When both--jsonand individual flags are supplied, individual flags are ignored. Use either flags or--json, never mix.- Nested objects need
--json: Per-servicemetadata.*andassets.*cannot be set via flat CLI flags. Always use--jsonor--input <file>. - Strip control characters: ASCII control characters (
U+0000–U+001Fexcept whitespace) cause payload rejections.
5. Per-Service Minimum Requirements & Threading
- Twitter / X, Threads, Bluesky, Mastodon: Require
textonly. - LinkedIn:
textorassets; documents requiremetadata.linkedin.linkAttachment. - Instagram: Image or video asset required; must specify
metadata.instagram.typeandmetadata.instagram.shouldShareToFeed. - Twitter / X Threads: When chaining posts via
metadata.twitter.thread, the top-leveltextMUST match the first thread item'stext:{ "channelId": "ch_123", "text": "First tweet in thread", "metadata": { "twitter": { "thread": [ { "text": "First tweet in thread" }, { "text": "Second tweet in thread" } ] } } }