# Buffer

> Manage, draft, schedule, and publish social media content across connected channels (such as LinkedIn, X/Twitter, Bluesky, and others) using the Buffer CLI (@bufferapp/cli). Covers account and channel inspection, queue scheduling with dry-run safety validation, draft ideas management, and GraphQL schema introspection. Activate when scheduling social media posts, inspecting Buffer channels, automating social publishing, or managing social queues.

- Skill: `danicat/buffer` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add danicat/buffer`
- Raw SKILL.md: https://api.skillmd.com/api/skills/danicat/buffer/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- License: Apache-2.0
- Author: danicat (https://skillmd.com/u/danicat)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/danicat/buffer

---


# 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](references/workflows.md) for shell scripting patterns, timezone math, and Relay cursor pagination.
- **Rate Limits & Idempotency**: Read [references/rate_limits.md](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:

```bash
# 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`:
> ```bash
> npx -y @bufferapp/cli doctor
> ```

### Authentication Modes

1. **Environment Variable (Recommended for CI / Ephemeral Agents):**
   ```bash
   export BUFFER_API_KEY="your-api-key"
   ```
2. **Global Configuration (`buffer init`):**
   ```bash
   buffer init
   ```
   *Writes API token, default organization, and timezone to `$XDG_CONFIG_HOME/buffer/config.json` (or `~/.config/buffer/config.json`).*

---

## 2. Core Operational Workflows

> [!IMPORTANT]
> Always use `--output json` when 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 `channelId`s:

```bash
# 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:

```bash
# 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:

```json
{
  "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:
```json
{
  "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:

```bash
# 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:

```bash
# 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

```bash
# 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:

```bash
# 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:

```bash
# 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.url` or `assets[].video.url`) **must be hosted publicly via HTTP/HTTPS** (e.g. S3, GCS, CDN, or raw GitHub link).
- **Empty `text` without assets is rejected**: Most channels require text or at least one image/video asset.

### 2. Scheduling Modes & Notification Traps
- **`mode: addToQueue` is queued, not immediate**: Use `mode: shareNow` to publish immediately. `mode: shareNext` jumps to the front of the queue. `mode: customScheduled` requires `dueAt`.
- **`schedulingType: notification` does not auto-publish**: It only sends a push notification to the user's mobile app. Always use `schedulingType: automatic` for hands-off publishing.
- **`addToQueue` on a channel with no schedule**: Silently lands in an empty queue slot without scheduling. Check the schedule with `buffer channels get --id <id>` first.
- **All times must be ISO-8601 with offset**: `dueAt` requires a timezone offset (e.g. `2026-05-06T17:00:00-05:00`). Obtain the offset from `buffer config get timezone` or `buffer 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 `channelId` from Organization A cannot be used while authenticated against Organization B.

### 4. Input & Formatting Constraints
- **`--json` overrides flags entirely**: When both `--json` and individual flags are supplied, individual flags are ignored. Use either flags or `--json`, never mix.
- **Nested objects need `--json`**: Per-service `metadata.*` and `assets.*` cannot be set via flat CLI flags. Always use `--json` or `--input <file>`.
- **Strip control characters**: ASCII control characters (`U+0000`–`U+001F` except whitespace) cause payload rejections.

### 5. Per-Service Minimum Requirements & Threading
- **Twitter / X, Threads, Bluesky, Mastodon**: Require `text` only.
- **LinkedIn**: `text` or `assets`; documents require `metadata.linkedin.linkAttachment`.
- **Instagram**: Image or video asset required; must specify `metadata.instagram.type` and `metadata.instagram.shouldShareToFeed`.
- **Twitter / X Threads**: When chaining posts via `metadata.twitter.thread`, the top-level `text` **MUST** match the first thread item's `text`:
  ```json
  {
    "channelId": "ch_123",
    "text": "First tweet in thread",
    "metadata": {
      "twitter": {
        "thread": [
          { "text": "First tweet in thread" },
          { "text": "Second tweet in thread" }
        ]
      }
    }
  }
  ```

