Skip to content
Letterhead Letterhead Letterhead Help Center
Admin Tools

Using the Letterhead API

Edit in CMS

The Letterhead API lets you work with your newsletters from your own code — your audience, your content, your sending, and your reporting. Everything you can do in the dashboard, you can build around.

Every request needs an API key sent as a Bearer token in the Authorization header:

Authorization: Bearer <your-api-key>

One key reaches every newsletter in your account, so you don’t need a separate key per newsletter. See Generate & manage API keys to create one.

Most read endpoints also expect api=true in the query string. It tells Letterhead you’re authenticating with a key rather than a signed-in session, and the reference notes it wherever it applies.

Requests go to the same subdomain you sign in with — just swap api in for app:

  • If you sign in at https://{yourPrefix}.app.tryletterhead.com, your API base URL is https://{yourPrefix}.api.tryletterhead.com. For example, acme.app.tryletterhead.com becomes acme.api.tryletterhead.com.
  • If you sign in at the plain https://app.tryletterhead.com (no subdomain), your API base URL is the plain https://api.tryletterhead.com.
  • Your audience — add, update, and remove contacts, set their subscription status per newsletter, and tag people in bulk
  • Segments — create and update saved audience groups, count who’s in one, preview the members, and pull engagement over time — or list Letterhead’s built-in system segments (see below) instead of hard-coding their IDs
  • Content — push articles into your curation library and distribute them across newsletters, including in bulk for many articles in one request
  • Sending — draft a letter from your own HTML or from a template, then send it — see Sending your own HTML for what’s tracked automatically and what can get a send rejected
  • Your newsletters — list what you’ve published, across your whole account or a chosen few of your newsletters, and narrow the list to the ones carrying a tag you organize by (see below)
  • Promotions — create and manage the paid placements running inside your newsletters
  • Reporting — pull opens, clicks, bounces, and unsubscribes, with a daily breakdown and per-newsletter detail
  • Audience overlap — shared subscriber counts for every pair of newsletters, and combined unique reach for a selection

If you organize your newsletters with tags, you can ask for just the ones carrying a tag rather than pulling everything and sorting it out in your own code.

First list your tags to get their ids, then pass those ids when you list your newsletters:

GET /api/v3/letters/tags?api=true
GET /api/v3/letters?api=true&allChannels=true&tags[]=<tagId>

Two things worth knowing before you build against it:

  • Naming more than one tag narrows the list, it doesn’t widen it. Asking for two tags returns the newsletters carrying both of them, not the ones carrying either. To gather everything across several tags, ask for one tag at a time and combine the results yourself.
  • You can name up to 50 tags in a request, and the filter works the same way whichever set you’re listing — your templates, a chosen few newsletters, or everything at once.

The same filter works when you pull performance numbers for many newsletters at once. The metrics request takes its tags inside a filters object, and the total it returns counts only the newsletters that match:

POST /api/v3/letters/metrics?api=true
{ "filters": { "tags": ["<tagId>"] } }

Each tag must be sent as its id, as text, exactly as the tag list returns it — a number, an empty entry, or more than 50 tags is rejected rather than quietly ignored.

Alongside the segments you build yourself, every account has a set of built-in, read-only system segments — Loyalists, New & Engaged, Casual, Fading, Ghosts, Single Newsletter, Everyone but Ghosts, and (on some accounts) a 60-day version of Everyone but Ghosts. Each already sends to a reserved numeric ID, but until now the only way to find that number was to copy it out of the app’s URL. List them from their own endpoint instead:

GET /api/v3/contacts/segments/system?api=true

Each entry in the response carries an id — the number to pass as segment when scheduling or sending a letter — a stable key, and a display name. It’s a separate, read-only listing: it doesn’t affect GET /api/v3/contacts/segments above, and a system segment can’t be created, updated, or deleted.

If you’re building in JavaScript or TypeScript, you don’t have to write the requests by hand. Letterhead publishes an official package, @tryletterhead/sdk, with a ready-made, typed method for every operation in the API reference. Your editor suggests the methods and tells you what each one takes and returns, and it adds the api=true flag for you.

npm install @tryletterhead/sdk

It needs Node 18 or later, or any runtime with a built-in fetch. Create an API key as described above, then add a contact and tag them in a few lines:

import { createLetterheadClient } from '@tryletterhead/sdk';
const letterhead = createLetterheadClient({ apiKey: process.env.LETTERHEAD_API_KEY! });
await letterhead.contacts.upsert({ email: '[email protected]', firstName: 'Pat', tags: ['vip'] });
await letterhead.tags.addToContacts('webinar-attendee', { emails: ['[email protected]'] });

If your account signs in on its own subdomain, pass your API base URL (see above) as baseUrl. When a request fails, the method throws an error that carries the status and the response, so you can handle it in one place.

Keep your API key on a server. A company API key can read and change your whole audience, and anything in code that runs in a browser is public. Use the package in your own backend or a serverless function, and have a website or React app call that instead — never put the key in front-end code.

When the API changes, the package is updated and released again, so new endpoints arrive in a new version soon after they reach the API reference. Update the package to pick them up. Its page on npm has the full list of methods and more examples.

If you’d rather work from a terminal, or from a script, the letterhead command-line tool does the same job without any code. Every operation in the API reference is a command, so a new endpoint becomes a new command when the tool is next updated.

npm install -g @tryletterhead/cli
export LETTERHEAD_API_KEY=<your-api-key>
letterhead contacts upsert --email [email protected] --first-name Pat --tag vip
letterhead tags add-to-contacts --tag webinar-attendee --email [email protected]

It needs Node 18 or later, and reads your API key from the LETTERHEAD_API_KEY environment variable (or --api-key, which leaves the key in your shell history). If your account signs in on its own subdomain, set LETTERHEAD_BASE_URL to your API base URL (see above). It never saves your key anywhere.

  • letterhead --help lists the groups of commands, and letterhead <group> <command> --help shows what a command takes. A command’s options are the endpoint’s parameters; anything else can be sent as JSON with --body, from a file (--body @contact.json) or standard input (--body -).
  • Answers print as JSON on standard output. If the API returns an error, the error is printed to standard error and the command exits with a non-zero code, so scripts can tell something went wrong.
  • Commands that delete something ask you to type yes when you’re at a terminal. In a script, or anywhere else without one, they refuse unless you add --yes, so nothing is deleted by accident.
  • Commands that upload a file aren’t supported yet; use the TypeScript package for those.

The same advice as for the package applies: keep your API key out of shared shells and scripts.

If you’re working with an AI coding agent, you can point it at this documentation in a form it can read without picking apart web pages:

  • /llms.txt is a short index: what Letterhead is, how to authenticate, where the API description and the TypeScript package live, and a link to every article.
  • /llms-full.txt is every article in one Markdown file, for agents that can take it all in at once.
  • Every article also has a Markdown copy: add .md to its address (this page is at /api/using-the-letterhead-api.md).
  • The full API description is available as an OpenAPI file at /openapi.yaml.

These contain only the articles anyone can read on this site.

The complete endpoint reference — paths, parameters, request and response formats, and examples — is in the API reference on this site. It’s generated from the API’s own specification, so it stays in step with what the API actually does.

Still can’t find what you need? Contact support.