Skip to main content
Glama
buttondown.ts10 kB
import { execSync } from "child_process"; import { ButtondownEmailsResponse, ButtondownEmail, CreateEmailRequest, UpdateEmailRequest, ButtondownSubscribersResponse, ButtondownTagsResponse, ButtondownScheduledEmailsResponse, } from "./types"; export class ButtondownAPI { private apiKey: string; private baseUrl: string; constructor(apiKey?: string) { this.apiKey = apiKey || ""; this.baseUrl = "https://api.buttondown.email"; } async initialize(): Promise<void> { if (!this.apiKey) { this.apiKey = await this.getApiKey(); } } private async getApiKey(): Promise<string> { // First try environment variable const envKey = process.env.BUTTONDOWN_API_KEY; if (envKey) { return envKey; } // Fallback to 1Password try { const opKey = execSync( 'op read "op://Development/Buttondown API/notesPlain"', { encoding: "utf-8", } ).trim(); if (opKey) { return opKey; } } catch (error) { console.error( "Failed to read from 1Password:", error instanceof Error ? error.message : "Unknown error" ); } throw new Error( "Could not find Buttondown API key in environment or 1Password" ); } async getEmails(): Promise<ButtondownEmailsResponse> { await this.initialize(); const response = await fetch("https://api.buttondown.email/v1/emails", { headers: { Authorization: `Token ${this.apiKey}`, "Content-Type": "application/json", }, }); if (!response.ok) { const errorText = await response.text(); throw new Error( `API request failed: ${response.status} ${response.statusText}\n${errorText}` ); } const data = await response.json(); return data as ButtondownEmailsResponse; } async getDrafts(): Promise<ButtondownEmailsResponse> { await this.initialize(); const response = await fetch( "https://api.buttondown.email/v1/emails?status=draft", { headers: { Authorization: `Token ${this.apiKey}`, "Content-Type": "application/json", }, } ); if (!response.ok) { const errorText = await response.text(); throw new Error( `API request failed: ${response.status} ${response.statusText}\n${errorText}` ); } const data = await response.json(); return data as ButtondownEmailsResponse; } async createEmail(email: CreateEmailRequest): Promise<ButtondownEmail> { await this.initialize(); // Normalize line breaks to \n const normalizedEmail = { ...email, body: email.body.replace(/\r\n/g, "\n"), }; const response = await fetch("https://api.buttondown.email/v1/emails", { method: "POST", headers: { Authorization: `Token ${this.apiKey}`, "Content-Type": "application/json", }, body: JSON.stringify(normalizedEmail), }); if (!response.ok) { const errorText = await response.text(); throw new Error( `API request failed: ${response.status} ${response.statusText}\n${errorText}` ); } const data = await response.json(); return data as ButtondownEmail; } async updateEmail( id: string, email: UpdateEmailRequest ): Promise<ButtondownEmail> { await this.initialize(); const response = await fetch( `https://api.buttondown.email/v1/emails/${id}`, { method: "PATCH", headers: { Authorization: `Token ${this.apiKey}`, "Content-Type": "application/json", }, body: JSON.stringify(email), } ); if (!response.ok) { const errorText = await response.text(); throw new Error( `API request failed: ${response.status} ${response.statusText}\n${errorText}` ); } const data = await response.json(); return data as ButtondownEmail; } async deleteEmail(id: string): Promise<void> { await this.initialize(); const response = await fetch( `https://api.buttondown.email/v1/emails/${id}`, { method: "DELETE", headers: { Authorization: `Token ${this.apiKey}`, "Content-Type": "application/json", }, } ); if (!response.ok) { const errorText = await response.text(); throw new Error( `API request failed: ${response.status} ${response.statusText}\n${errorText}` ); } } async getEmail(id: string): Promise<ButtondownEmail> { await this.initialize(); const response = await fetch( `https://api.buttondown.email/v1/emails/${id}`, { headers: { Authorization: `Token ${this.apiKey}`, "Content-Type": "application/json", }, } ); if (!response.ok) { const errorText = await response.text(); throw new Error( `API request failed: ${response.status} ${response.statusText}\n${errorText}` ); } const data = await response.json(); return data as ButtondownEmail; } async getSubscribers(): Promise<ButtondownSubscribersResponse> { await this.initialize(); const response = await fetch( "https://api.buttondown.email/v1/subscribers", { headers: { Authorization: `Token ${this.apiKey}`, "Content-Type": "application/json", }, } ); if (!response.ok) { const errorText = await response.text(); throw new Error( `API request failed: ${response.status} ${response.statusText}\n${errorText}` ); } const data = await response.json(); return data as ButtondownSubscribersResponse; } async getTags(): Promise<ButtondownTagsResponse> { await this.initialize(); const response = await fetch("https://api.buttondown.email/v1/tags", { headers: { Authorization: `Token ${this.apiKey}`, "Content-Type": "application/json", }, }); if (!response.ok) { const errorText = await response.text(); throw new Error( `API request failed: ${response.status} ${response.statusText}\n${errorText}` ); } const data = await response.json(); return data as ButtondownTagsResponse; } async getScheduledEmails(): Promise<ButtondownScheduledEmailsResponse> { await this.initialize(); const response = await fetch( "https://api.buttondown.email/v1/emails?status=scheduled", { headers: { Authorization: `Token ${this.apiKey}`, "Content-Type": "application/json", }, } ); if (!response.ok) { const errorText = await response.text(); throw new Error( `API request failed: ${response.status} ${response.statusText}\n${errorText}` ); } const data = await response.json(); return data as ButtondownScheduledEmailsResponse; } async scheduleEmail( id: string, scheduledFor: string ): Promise<ButtondownEmail> { await this.initialize(); const response = await fetch( `https://api.buttondown.email/v1/emails/${id}`, { method: "PATCH", headers: { Authorization: `Token ${this.apiKey}`, "Content-Type": "application/json", }, body: JSON.stringify({ status: "scheduled", scheduled_for: scheduledFor, }), } ); if (!response.ok) { const errorText = await response.text(); throw new Error( `API request failed: ${response.status} ${response.statusText}\n${errorText}` ); } const data = await response.json(); return data as ButtondownEmail; } async unscheduleEmail(id: string): Promise<ButtondownEmail> { await this.initialize(); const response = await fetch( `https://api.buttondown.email/v1/emails/${id}`, { method: "PATCH", headers: { Authorization: `Token ${this.apiKey}`, "Content-Type": "application/json", }, body: JSON.stringify({ status: "draft", scheduled_for: null, }), } ); if (!response.ok) { const errorText = await response.text(); throw new Error( `API request failed: ${response.status} ${response.statusText}\n${errorText}` ); } const data = await response.json(); return data as ButtondownEmail; } async getRawTimeseriesAnalytics( startDate: string, endDate: string, interval: "day" | "week" | "month" = "day" ): Promise<any> { await this.initialize(); const response = await fetch( `https://api.buttondown.email/v1/analytics/timeseries?start_date=${startDate}&end_date=${endDate}&interval=${interval}`, { headers: { Authorization: `Token ${this.apiKey}`, "Content-Type": "application/json", }, } ); if (!response.ok) { const errorText = await response.text(); throw new Error( `API request failed: ${response.status} ${response.statusText}\n${errorText}` ); } const data = await response.json(); return data; } private async get(endpoint: string): Promise<any> { const response = await fetch(`${this.baseUrl}${endpoint}`, { method: "GET", headers: { Authorization: `Token ${this.apiKey}`, "Content-Type": "application/json", }, }); if (!response.ok) { const errorText = await response.text(); throw new Error( `API request failed: ${response.status} ${response.statusText}\n${errorText}` ); } const data = await response.json(); return data; } async getEmailAnalytics(): Promise<ButtondownEmailsResponse> { // Get analytics directly from email responses return this.getEmails(); } async getEmailStats(emailId: string): Promise<ButtondownEmail> { // Get stats directly from email response return this.getEmail(emailId); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/The-Focus-AI/buttondown-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server