Skip to content
Letterhead Letterhead Letterhead Help Center
Admin Tools

Code samples

Edit in CMS

Each task below is shown three ways: with the TypeScript package, with the letterhead command line, and as a plain HTTP request with curl. To set up the package or the command line, see Installing the TypeScript SDK and CLI. For every parameter, see the API reference.

The TypeScript examples assume a client created like this:

import { createLetterheadClient } from '@tryletterhead/sdk';
const letterhead = createLetterheadClient({ apiKey: process.env.LETTERHEAD_API_KEY! });

The curl examples use $LETTERHEAD_API_KEY for your key and https://acme.api.tryletterhead.com for your API address; swap in your own (see Getting started with the Letterhead API). Where a newsletter is named, use its slug, such as my-newsletter.

Add a contact and subscribe them to a newsletter

Section titled “Add a contact and subscribe them to a newsletter”

Adding a contact puts them in your company audience, or updates them if a contact with that email already exists. Subscribing them to a newsletter is a second step. A subscription status of 1 means subscribed.

await letterhead.contacts.upsert({ email: '[email protected]', firstName: 'Pat', tags: ['vip'] });
await letterhead.contacts.createASubscription({
path: { email: '[email protected]' },
body: { channel: 'my-newsletter', status: 1 },
});
Terminal window
letterhead contacts upsert --email [email protected] --first-name Pat --tag vip
letterhead contacts create-a-subscription --email [email protected] --channel my-newsletter --status 1
Terminal window
curl -X POST "https://acme.api.tryletterhead.com/api/v3/contacts" \
-H "Authorization: Bearer $LETTERHEAD_API_KEY" \
-H "Content-Type: application/json" \
-d '{"api": true, "email": "[email protected]", "firstName": "Pat", "tags": ["vip"]}'
curl -X POST "https://acme.api.tryletterhead.com/api/v3/contacts/person%40example.org/subscriptions" \
-H "Authorization: Bearer $LETTERHEAD_API_KEY" \
-H "Content-Type: application/json" \
-d '{"api": true, "channel": "my-newsletter", "status": 1}'

Tags you name are applied to the contact. Custom field values go in customFields, keyed by a field name that already exists for your company.

Curating a story adds it to your company’s curation library, ready to drop into a newsletter. Letterhead saves exactly the fields you send and doesn’t read the page for you, so pass the title, excerpt and image yourself. Name newsletters in channels[] to share the story with only those; leave it out to share it with every newsletter. Sending the same url again updates the story rather than adding a second copy.

const { items } = await letterhead.curations.curateOrUpdateContent({
body: {
url: 'https://example.org/stories/spring-openings',
title: 'Spring openings',
excerpt: 'Two new galleries open downtown this weekend.',
image: 'https://example.org/images/spring.jpg',
'channels[]': ['my-newsletter'],
},
});
Terminal window
letterhead curations curate-or-update-content \
--url https://example.org/stories/spring-openings \
--title "Spring openings" \
--excerpt "Two new galleries open downtown this weekend." \
--image https://example.org/images/spring.jpg \
--channels my-newsletter
Terminal window
curl -X POST "https://acme.api.tryletterhead.com/api/v3/curations" \
-H "Authorization: Bearer $LETTERHEAD_API_KEY" \
-F "api=true" \
-F "url=https://example.org/stories/spring-openings" \
-F "title=Spring openings" \
-F "excerpt=Two new galleries open downtown this weekend." \
-F "image=https://example.org/images/spring.jpg" \
-F "channels[]=my-newsletter"

The response’s items.uniqueId identifies the story for the other curation endpoints. To add many stories in one request, see Adding curations in bulk.

You can send a newsletter from your own HTML. It’s queued as soon as the request succeeds and goes out within about a minute, so send to yourself first. A segment of 0 sends to every subscriber of the newsletter.

const html = `<!DOCTYPE html><html><body>
<h1>Spring openings</h1>
<p>Two new galleries open downtown this weekend. <a href="https://example.org/stories/spring-openings">Read more</a></p>
<p><a href="{{ unsubscribe }}">Unsubscribe</a></p>
</body></html>`;
await letterhead.letters.sendALetter({
body: { channel: 'my-newsletter', subject: 'Spring openings', subtitle: 'Two new galleries this weekend', html, segment: 0 },
});
Terminal window
letterhead letters send-a-letter --body @letter.json

where letter.json holds the same fields:

{
"channel": "my-newsletter",
"subject": "Spring openings",
"subtitle": "Two new galleries this weekend",
"segment": 0,
"html": "<!DOCTYPE html><html><body><h1>Spring openings</h1><p><a href=\"{{ unsubscribe }}\">Unsubscribe</a></p></body></html>"
}
Terminal window
curl -X POST "https://acme.api.tryletterhead.com/api/v3/letters/actions/send" \
-H "Authorization: Bearer $LETTERHEAD_API_KEY" \
-H "Content-Type: application/json" \
-d @letter.json

For curl, add "api": true to letter.json; the package and the command line add it for you.

Before you send, know these:

  • Add your own unsubscribe link. Sending your own HTML never adds one for you. Use the {{ unsubscribe }} merge tag, as above.
  • The newsletter must send through Letterhead. If it sends through another email service, the request is refused. Create a draft instead with createADraft (letterhead letters create-a-draft), which takes the same fields, then test, schedule or send it from Letterhead.
  • Opens and clicks are tracked for you, with a few limits, and some HTML is rejected.

Sending your own HTML covers tracking, merge tags and what can get a send refused.

If a request fails in a way the error message doesn’t explain, contact support.

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