Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/buttondown/cli/llms.txt

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

The push command uploads your local content changes to Buttondown, intelligently detecting what has changed and only updating what’s necessary.

How Push Works

When you run buttondown push, the CLI performs an intelligent sync:
1

Read Sync State

Loads the .buttondown-sync.json file to understand which images have already been uploaded and their URL mappings.
2

Process Local Emails

Reads all email files from the emails/ directory and identifies any relative image references that need to be uploaded.
3

Upload New Images

Automatically uploads any images referenced in emails that haven’t been synced yet to Buttondown’s media library.
4

Convert Image References

Replaces relative image paths with absolute URLs from Buttondown before uploading email content.
5

Detect Changes

Compares local emails with remote versions to identify only the emails that have actually changed.
6

Push Changes

Updates changed emails and pushes automations, snippets, and newsletter settings.
7

Update Sync State

Updates the .buttondown-sync.json file with any newly uploaded images.

Basic Usage

Push all local changes to Buttondown:
buttondown push
Make sure you’ve run buttondown pull at least once before pushing. The sync state file is required for proper image handling.

What Gets Pushed

Changed Emails Only

The push command is smart about detecting changes. It only uploads emails that have been modified since the last sync:
// The CLI compares serialized versions to detect changes
const changedEmails = localEmails.filter((email) => {
  const remote = remoteEmailsById.get(email.id);
  if (!remote) return true; // New email
  return serialize(email) !== serialize(remote); // Changed email
});
This means you can safely run push frequently without worrying about unnecessary API calls or updates.

Automatic Image Upload

When you add new images to your emails using relative paths, push automatically handles the upload:
---
subject: New Product Launch
---

Check out our new product!

![Product Photo](../media/new-product.png)
The CLI:
  1. Finds the relative image reference ../media/new-product.png
  2. Uploads new-product.png to Buttondown
  3. Receives the remote URL (e.g., https://buttondown.s3.amazonaws.com/images/xyz789.png)
  4. Replaces the relative path with the remote URL in the email content
  5. Pushes the updated email

Automations

Modified automation files in automations/ are pushed to Buttondown:
  • Existing automations (with an id field) are updated via PATCH
  • New automations (without an id) are created via POST
automations/onboarding.json
{
  "name": "Onboarding Sequence",
  "status": "active",
  "trigger": {
    "type": "subscriber_created"
  },
  "actions": [
    {
      "type": "send_email",
      "delay": 0,
      "email_id": "welcome-day-1"
    },
    {
      "type": "send_email",
      "delay": 86400,
      "email_id": "welcome-day-2"
    }
  ]
}

Snippets

Snippets in snippets/ are synced based on their identifier:
snippets/social-links.md
---
id: snip_abc123
name: Social Media Links
---

Follow us:
- Twitter: [@example](https://twitter.com/example)
- LinkedIn: [Company](https://linkedin.com/company/example)

Newsletter Settings

Changes to newsletter.json update your newsletter configuration:
newsletter.json
{
  "name": "Tech Weekly",
  "description": "Your weekly dose of tech insights",
  "author": "Jane Doe",
  "email_address": "hello@techweekly.com"
}

Image Handling Details

Supported Image Formats

The push command supports uploading these image types:
  • PNG (.png)
  • JPEG (.jpg, .jpeg)
  • GIF (.gif)
  • WebP (.webp)
  • SVG (.svg)
Images are uploaded with the correct MIME type based on their file extension.

Image Upload Process

From the source code at src/sync/images.ts:24:
export async function uploadImage(
  configuration: Configuration,
  imagePath: string,
): Promise<{ id: string; url: string; filename: string }> {
  const buffer = await readFile(imagePath);
  const filename = path.basename(imagePath);
  const ext = path.extname(imagePath).toLowerCase();
  const mimeType = EXTENSION_TO_MIME[ext] || "application/octet-stream";

  const formData = new FormData();
  formData.append(
    "image",
    new Blob([new Uint8Array(buffer)], { type: mimeType }),
    filename,
  );

  const response = await constructClient(configuration).post("/images", {
    body: formData,
  });
  // ...
}

Relative to Absolute Conversion

The CLI finds all relative image references and converts them:
<!-- Local version -->
![Dashboard](../media/screenshots/dashboard.png)

<!-- Pushed to Buttondown -->
![Dashboard](https://buttondown.s3.amazonaws.com/images/dashboard.png)
This happens automatically in src/commands/push.tsx:138:
const changedEmails = localEmails
  .map((email) => ({
    ...email,
    body: email.body
      ? resolveRelativeImageReferences(email.body, emailsDir, imageMap)
      : email.body,
  }))

Push Results

After pushing, you’ll see detailed results:
emails pushed: 3 updated, 1 created, 0 deleted, 0 failed
automations pushed: 1 updated, 0 created, 0 deleted, 0 failed
newsletter pushed: 1 updated, 0 created, 0 deleted, 0 failed
snippets pushed: 0 updated, 1 created, 0 deleted, 0 failed

Best Practices

Always run buttondown pull before making changes and pushing. This ensures you have the latest content and prevents conflicts.
buttondown pull
# Make your changes
buttondown push
Preview your Markdown content locally before pushing to ensure formatting is correct.
Commit your changes to Git before pushing to Buttondown. This creates a history of your newsletter content.
git add .
git commit -m "Update welcome email copy"
buttondown push
Make sure relative image paths are correct before pushing. The image file must exist in your media/ directory.
# Correct - relative to emails/ directory
![Logo](../media/logo.png)

# Wrong - absolute path (don't do this)
![Logo](&lt;absolute-path&gt;/media/logo.png)

Troubleshooting

”Image upload failed”

Make sure:
  • The image file exists at the specified path
  • The file is a supported image format
  • You have permission to read the file

”Email already exists”

If you’re creating a new email without an id field but an email with that slug already exists on Buttondown, you’ll get an error. Pull first to get existing email IDs.

”Sync state not found”

Run buttondown pull first to initialize the sync state file:
buttondown pull

Next Steps

Pull Content

Learn how to pull content from Buttondown

Manage Emails

Create and edit newsletter emails