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 newsletter module handles synchronization of newsletter settings between the Buttondown API and a local JSON file.

Overview

The newsletter configuration represents the top-level settings for your Buttondown newsletter. Unlike other resources that handle collections, this module manages a single newsletter object.

Types

Newsletter

Based on the Buttondown API Newsletter schema from OpenAPI specification.
type Newsletter = components["schemas"]["Newsletter"]
Typically includes:
  • id - Unique newsletter identifier
  • username - Newsletter username/slug
  • name - Newsletter display name
  • description - Newsletter description
  • settings - Various configuration options
  • creation_date - When the newsletter was created
And many other configuration fields for branding, behavior, and integrations.

Resource Objects

REMOTE_NEWSLETTER_RESOURCE

Handles newsletter operations with the Buttondown API.
const REMOTE_NEWSLETTER_RESOURCE: Resource<Newsletter, Newsletter>

Methods

get(configuration) Fetches the newsletter configuration from the API.
async get(configuration: Configuration): Promise<Newsletter | null>
configuration
Configuration
required
Configuration with API credentials and username
return
Newsletter | null
Newsletter object matching the configured username, or null if not found

Behavior

  • Fetches all newsletters from the API (GET /newsletters)
  • Filters results to find newsletter matching configuration.username
  • Returns null if no matching newsletter is found

Example

import { REMOTE_NEWSLETTER_RESOURCE } from "./sync/newsletter.js";

const config = {
  baseUrl: "https://api.buttondown.email/v1",
  apiKey: "your-api-key",
  username: "my-newsletter",
  directory: "./buttondown-data"
};

const newsletter = await REMOTE_NEWSLETTER_RESOURCE.get(config);

if (newsletter) {
  console.log(`Found newsletter: ${newsletter.name}`);
  console.log(`Username: ${newsletter.username}`);
} else {
  console.error("Newsletter not found");
}
set(value, configuration) Updates the newsletter configuration via the API.
async set(
  value: Newsletter,
  configuration: Configuration
): Promise<OperationResult>
value
Newsletter
required
Newsletter object with updated values (must include id)
configuration
Configuration
required
Configuration with API credentials
return
OperationResult
Operation result indicating successful update

Behavior

  • Sends PATCH request to /newsletters/{id}
  • Updates newsletter with provided fields
  • Always returns:
    {
      updated: 1,
      created: 0,
      deleted: 0,
      failed: 0
    }
    

Example

const newsletter = await REMOTE_NEWSLETTER_RESOURCE.get(config);

if (newsletter) {
  // Update settings
  newsletter.name = "My Updated Newsletter";
  newsletter.description = "New description";
  
  const result = await REMOTE_NEWSLETTER_RESOURCE.set(
    newsletter,
    config
  );
  
  console.log(`Updated: ${result.updated}`);
}

LOCAL_NEWSLETTER_RESOURCE

Handles newsletter operations with local JSON file.
const LOCAL_NEWSLETTER_RESOURCE: Resource<Newsletter, Newsletter>

Methods

get(configuration) Reads newsletter configuration from local newsletter.json file.
async get(configuration: Configuration): Promise<Newsletter>
configuration
Configuration
required
Configuration with directory path
return
Newsletter
Newsletter object parsed from JSON file

Behavior

  • Reads from {directory}/newsletter.json
  • Parses JSON content
  • Returns newsletter object
  • Throws error if file doesn’t exist or JSON is invalid

Example

import { LOCAL_NEWSLETTER_RESOURCE } from "./sync/newsletter.js";

const config = {
  directory: "./buttondown-data",
  baseUrl: "https://api.buttondown.email/v1",
  apiKey: "your-api-key"
};

try {
  const newsletter = await LOCAL_NEWSLETTER_RESOURCE.get(config);
  console.log(`Local newsletter: ${newsletter.name}`);
} catch (error) {
  console.error("Failed to read newsletter.json");
}
set(value, configuration) Writes newsletter configuration to local newsletter.json file.
async set(
  value: Newsletter,
  configuration: Configuration
): Promise<OperationResult>
value
Newsletter
required
Newsletter object to write
configuration
Configuration
required
Configuration with directory path
return
OperationResult
Operation result indicating successful write

Behavior

  • Writes to {directory}/newsletter.json
  • Formats JSON with 2-space indentation
  • Creates file if it doesn’t exist
  • Overwrites existing file
  • Always returns:
    {
      updated: 1,
      created: 0,
      deleted: 0,
      failed: 0
    }
    

Example

const newsletter = {
  id: "news_123",
  username: "my-newsletter",
  name: "My Newsletter",
  description: "A great newsletter",
  // ... other fields
};

await LOCAL_NEWSLETTER_RESOURCE.set(newsletter, config);
console.log("Newsletter saved to newsletter.json");

NEWSLETTER_RESOURCE

Combined resource group for newsletter synchronization.
const NEWSLETTER_RESOURCE: ResourceGroup<
  Newsletter,
  Newsletter,
  Newsletter
>

Properties

name
string
Resource identifier: "newsletter"
remote
Resource<Newsletter, Newsletter>
Remote API resource handler
local
Resource<Newsletter, Newsletter>
Local filesystem resource handler

Usage Patterns

Pull Newsletter from Remote

import { NEWSLETTER_RESOURCE } from "./sync/newsletter.js";

const config = {
  baseUrl: "https://api.buttondown.email/v1",
  apiKey: "your-api-key",
  username: "my-newsletter",
  directory: "./buttondown-data"
};

// Fetch from API
const newsletter = await NEWSLETTER_RESOURCE.remote.get(config);

if (newsletter) {
  // Save to local file
  await NEWSLETTER_RESOURCE.local.set(newsletter, config);
  console.log("Newsletter synced to newsletter.json");
} else {
  console.error("Newsletter not found on remote");
}

Push Local Changes to Remote

// Read from local file
const localNewsletter = await NEWSLETTER_RESOURCE.local.get(config);

// Update API
const result = await NEWSLETTER_RESOURCE.remote.set(
  localNewsletter,
  config
);

console.log(`Newsletter updated: ${result.updated}`);

Edit Newsletter Settings

import { readFile, writeFile } from "fs/promises";
import path from "path";

const config = {
  directory: "./buttondown-data",
  baseUrl: "https://api.buttondown.email/v1",
  apiKey: "your-api-key"
};

// Read current settings
const filePath = path.join(config.directory, "newsletter.json");
const newsletter = JSON.parse(
  await readFile(filePath, "utf8")
);

// Update settings
newsletter.name = "My New Newsletter Name";
newsletter.description = "Updated description";

// Write back
await writeFile(
  filePath,
  JSON.stringify(newsletter, null, 2)
);

console.log("Newsletter settings updated locally");

Sync Workflow

// 1. Pull latest from remote
const remote = await NEWSLETTER_RESOURCE.remote.get(config);
if (!remote) {
  throw new Error("Newsletter not found");
}

// 2. Save backup locally
await NEWSLETTER_RESOURCE.local.set(remote, config);

// 3. Make local changes
const local = await NEWSLETTER_RESOURCE.local.get(config);
local.description = "Updated locally";
await NEWSLETTER_RESOURCE.local.set(local, config);

// 4. Push changes back to remote
await NEWSLETTER_RESOURCE.remote.set(local, config);

console.log("Sync complete");

Notes

Serialization

Unlike emails, automations, and snippets, the newsletter resource doesn’t require custom serialization/deserialization:
serialize: (r) => r,    // Identity function
deserialize: (s) => s   // Identity function
The newsletter object is stored as-is in JSON format without transformation.

Single Resource

The newsletter module differs from other sync operations:
  • Handles a single object instead of a collection
  • Returns Newsletter | null instead of Newsletter[]
  • No pagination needed
  • No slug/identifier generation required

Username Matching

When fetching from remote, the module filters newsletters by configuration.username. Make sure your configuration includes the correct username:
const config = {
  username: "my-newsletter",  // Must match your Buttondown username
  // ... other config
};