NoosletterNoosletter

Start here

  • About Noosletter
  • Contact

Guides

  • How points and levels work
  • How newsletter points and rankings work
  • Sending reputation
  • Plans and pricing
  • Account limits
  • How we check email addresses
  • Checks before you send
  • Sending domains
  • Inviting and importing subscribers
  • Trash, archive and deleting
  • Team roles

Developers

  • API and webhooks

Legal

  • Terms of service
  • Privacy policy
  • Cookie policy
API and webhooks

Start here

  • About Noosletter
  • Contact

Guides

  • How points and levels work
  • How newsletter points and rankings work
  • Sending reputation
  • Plans and pricing
  • Account limits
  • How we check email addresses
  • Checks before you send
  • Sending domains
  • Inviting and importing subscribers
  • Trash, archive and deleting
  • Team roles

Developers

  • API and webhooks

Legal

  • Terms of service
  • Privacy policy
  • Cookie policy

Noosletter API

Invite subscribers from your own systems, write and send emails, and hear about changes as they happen. Everything is scoped to one workspace and follows the same rules as the app. The API does not return who opened, clicked or visited.

StartErrorsAccountNewslettersSubscribersFormsTagsSegmentsEmailsAutomationsWebhooksCommand lineOpenAPI document

Start

Create a key in Settings → Developers. A read key can only read; a read and write key can also change things and send. Older Subscribers & tags keys still work: they can create, update and tag subscribers, and look up one by id or email, but nothing else. Keep keys on your server. Send the key as a bearer token:

curl https://app.noosletter.com/v1/newsletters \
  -H "Authorization: Bearer $NOOS_API_KEY"
  • Every response is JSON: { "data": … }, or { "error": { "type", "message" } }.
  • Field names are snake_case. Times are ISO 8601 in UTC.
  • Lists that can be long are paged: pass limit (up to 100) and the next_cursor you were given.
  • 120 requests a minute per key. Over that you get 429 with a Retry-After header.
  • Creating and sending take an Idempotency-Key header. Repeat it within 24 hours and you get the first response back instead of a second subscriber or a second send.

Errors

400 invalid_requestSomething in the request isn't right. The message says what.
401 unauthorizedThe key is missing, wrong or revoked.
403 forbiddenA read key tried to change something.
404 not_foundIt doesn't exist in this workspace.
409It conflicts with how things are, e.g. already_sent, already_exists, unsubscribed_by_reader.
422It can't be done as asked, e.g. invalid_email, blocked, not_ready, no_recipients.
429 rate_limitedSlow down; see Retry-After.

Account

GET /v1/me

Who am I

The workspace and key this request authenticated as. Use it to test a key.

Any key

curl -X GET https://app.noosletter.com/v1/me \
  -H "Authorization: Bearer $NOOS_API_KEY"

Newsletters

GET /v1/newsletters

List your newsletters

Read, or read and write key

curl -X GET https://app.noosletter.com/v1/newsletters \
  -H "Authorization: Bearer $NOOS_API_KEY"

GET /v1/newsletters/{id}

Get a newsletter

Read, or read and write key

curl -X GET https://app.noosletter.com/v1/newsletters/ID \
  -H "Authorization: Bearer $NOOS_API_KEY"

Subscribers

GET /v1/subscribers

List subscribers

Newest first.

Any key -- a Subscribers & tags key can only use ?email, not list or export

  • limit 1 to 100, default 25
  • cursor The next_cursor from the previous page
  • email Find one address -- the only way a free-tier Subscribers & tags key may call this endpoint (without it, 403)
  • newsletter_id Only subscribers of this newsletter
  • status With newsletter_id: subscribed (default) or unsubscribed
curl -X GET https://app.noosletter.com/v1/subscribers \
  -H "Authorization: Bearer $NOOS_API_KEY"

POST /v1/subscribers

Invite someone to subscribe

Nobody is added directly. This runs your Blocklist and address verification (an address that can't receive email is refused), then emails an invite for each newsletter in newsletter_ids. The person becomes a subscriber only when they accept it, and invites expire after 30 days. Someone who left a newsletter can't be invited back to it. Names, tags and fields aren't accepted: the subscriber fills in their own profile.

Read and write key, or a Subscribers & tags key · accepts Idempotency-Key

  • email string, required
  • first_name string
  • last_name string
  • newsletter_ids array of string. Invite them to these newsletters. They are subscribed only when they accept the invite email.
  • consent object. Contains consent.basis.
  • tag_ids array of string
curl -X POST https://app.noosletter.com/v1/subscribers \
  -H "Authorization: Bearer $NOOS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"email":"ada@example.com","first_name":"Ada","newsletter_ids":["NEWSLETTER_ID"],"consent":{"basis":"Ticked the box at checkout"}}'

GET /v1/subscribers/{id}

Get a subscriber

Any key

curl -X GET https://app.noosletter.com/v1/subscribers/ID \
  -H "Authorization: Bearer $NOOS_API_KEY"

PATCH /v1/subscribers/{id}

Update a subscriber's name

Read and write key, or a Subscribers & tags key

  • first_name object
  • last_name object
curl -X PATCH https://app.noosletter.com/v1/subscribers/ID \
  -H "Authorization: Bearer $NOOS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"first_name":"Ada"}'

POST /v1/subscribers/{id}/subscriptions

Invite a subscriber to another newsletter

Sends them an invite to this newsletter; they are subscribed only when they accept. Someone who left it can't be invited back (blocked).

Read and write key · accepts Idempotency-Key

  • newsletter_id string, required
  • consent object. Contains consent.basis.
curl -X POST https://app.noosletter.com/v1/subscribers/ID/subscriptions \
  -H "Authorization: Bearer $NOOS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"newsletter_id":"NEWSLETTER_ID","consent":{"basis":"Asked at our event"}}'

DELETE /v1/subscribers/{id}/subscriptions/{newsletter_id}

Unsubscribe from a newsletter

Read and write key

curl -X DELETE https://app.noosletter.com/v1/subscribers/ID/subscriptions/NEWSLETTER_ID \
  -H "Authorization: Bearer $NOOS_API_KEY"

POST /v1/subscribers/{id}/tags

Tag a subscriber

Read and write key, or a Subscribers & tags key

  • tag_id string, required
curl -X POST https://app.noosletter.com/v1/subscribers/ID/tags \
  -H "Authorization: Bearer $NOOS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tag_id":"TAG_ID"}'

DELETE /v1/subscribers/{id}/tags/{tag_id}

Remove a tag

Read and write key, or a Subscribers & tags key

curl -X DELETE https://app.noosletter.com/v1/subscribers/ID/tags/TAG_ID \
  -H "Authorization: Bearer $NOOS_API_KEY"

GET /v1/stats/growth

Subscriber growth

New subscribers and unsubscribes this week (from Monday) and this month (from the 1st), on UK calendar days, and the current total. Totals only.

Read, or read and write key

  • newsletter_id Only this newsletter
curl -X GET https://app.noosletter.com/v1/stats/growth \
  -H "Authorization: Bearer $NOOS_API_KEY"

Forms

GET /v1/forms

List forms

Read, or read and write key

curl -X GET https://app.noosletter.com/v1/forms \
  -H "Authorization: Bearer $NOOS_API_KEY"

GET /v1/forms/{id}

Get a form

Read, or read and write key

curl -X GET https://app.noosletter.com/v1/forms/ID \
  -H "Authorization: Bearer $NOOS_API_KEY"

Tags

GET /v1/tags

List tags

Read, or read and write key

curl -X GET https://app.noosletter.com/v1/tags \
  -H "Authorization: Bearer $NOOS_API_KEY"

POST /v1/tags

Create a tag

Read and write key · accepts Idempotency-Key

  • name string, required
curl -X POST https://app.noosletter.com/v1/tags \
  -H "Authorization: Bearer $NOOS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name":"Customers"}'

Segments

GET /v1/segments

List segments

Read, or read and write key

curl -X GET https://app.noosletter.com/v1/segments \
  -H "Authorization: Bearer $NOOS_API_KEY"

GET /v1/segments/{id}

Get a segment and how many people are in it

Read, or read and write key

curl -X GET https://app.noosletter.com/v1/segments/ID \
  -H "Authorization: Bearer $NOOS_API_KEY"

Emails

GET /v1/emails

List emails

Read, or read and write key

  • limit 1 to 100, default 25
  • cursor The next_cursor from the previous page
  • status draft, scheduled, sending, sent, failed, cancelled or held_for_review
curl -X GET https://app.noosletter.com/v1/emails \
  -H "Authorization: Bearer $NOOS_API_KEY"

POST /v1/emails

Create a draft

Written in Markdown, the same as the editor. It's sent from your default verified sender.

Read and write key · accepts Idempotency-Key

  • newsletter_id string. Needed when the account has more than one newsletter
  • name string. Defaults to the subject
  • subject string
  • preview_text object
  • body_markdown string. The email, in Markdown
curl -X POST https://app.noosletter.com/v1/emails \
  -H "Authorization: Bearer $NOOS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"subject":"This week","body_markdown":"# Hello\n\nIt's been a busy week."}'

GET /v1/emails/{id}

Get an email

Read, or read and write key

curl -X GET https://app.noosletter.com/v1/emails/ID \
  -H "Authorization: Bearer $NOOS_API_KEY"

PATCH /v1/emails/{id}

Edit a draft

Read and write key

  • name string
  • newsletter_id string
  • subject string
  • preview_text object
  • body_markdown string
curl -X PATCH https://app.noosletter.com/v1/emails/ID \
  -H "Authorization: Bearer $NOOS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"subject":"This week, properly"}'

POST /v1/emails/{id}/schedule

Schedule

Checks there's a subject, a body, a verified sender and your postal address first.

Read and write key · accepts Idempotency-Key

  • send_at string, required. When to send, ISO 8601 with a time zone offset
  • timezone string. IANA time zone to show it in, e.g. Europe/London. Defaults to the account's.
curl -X POST https://app.noosletter.com/v1/emails/ID/schedule \
  -H "Authorization: Bearer $NOOS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"send_at":"2026-10-02T08:00:00+01:00"}'

POST /v1/emails/{id}/cancel

Cancel a schedule

It goes back to being a draft.

Read and write key

curl -X POST https://app.noosletter.com/v1/emails/ID/cancel \
  -H "Authorization: Bearer $NOOS_API_KEY"

POST /v1/emails/{id}/send

Send now

Sends to everyone subscribed to the email's newsletter who can receive it. Pass an Idempotency-Key so a retry can never send twice; without one, a second send is refused.

Read and write key · accepts Idempotency-Key

curl -X POST https://app.noosletter.com/v1/emails/ID/send \
  -H "Authorization: Bearer $NOOS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

GET /v1/emails/{id}/stats

Stats

Per-email totals. The API does not return who opened or clicked.

Read, or read and write key

curl -X GET https://app.noosletter.com/v1/emails/ID/stats \
  -H "Authorization: Bearer $NOOS_API_KEY"

POST /v1/emails/{id}/test

Send a test to yourself

Sends a test of the email to the person the key belongs to, and nobody else. A test never counts towards a sending limit. A key that isn't tied to a person gets 422 no_person.

Read and write key

curl -X POST https://app.noosletter.com/v1/emails/ID/test \
  -H "Authorization: Bearer $NOOS_API_KEY"

GET /v1/emails/{id}/recipients

Recipient count

How many subscribers would get this email if it went out now, after every sendability check. A count only.

Read, or read and write key

curl -X GET https://app.noosletter.com/v1/emails/ID/recipients \
  -H "Authorization: Bearer $NOOS_API_KEY"

GET /v1/stats/top-emails

Top emails

Your best-performing recent sent emails (the latest 200), by unique opens or clickers. Totals only.

Read, or read and write key

  • by opens (default) or clicks
  • limit 1 to 20, default 5
  • newsletter_id Only this newsletter's emails
curl -X GET https://app.noosletter.com/v1/stats/top-emails \
  -H "Authorization: Bearer $NOOS_API_KEY"

Automations

GET /v1/automations

List automation rules

Read-only. Comes with Pro (upgrade a newsletter to Pro to use it). There's no way to create or edit a rule through the API yet.

Read, or read and write key

curl -X GET https://app.noosletter.com/v1/automations \
  -H "Authorization: Bearer $NOOS_API_KEY"

GET /v1/automations/{id}

Get an automation rule

Read-only, for the same reason as the list endpoint.

Read, or read and write key

curl -X GET https://app.noosletter.com/v1/automations/ID \
  -H "Authorization: Bearer $NOOS_API_KEY"

POST /v1/automations/{id}/run

Run an automation

Runs a rule that starts with "Run on selected subscribers" for people who are already your subscribers, the same as "Run automation…" on the Subscribers page. The rule must be on. It never subscribes anyone, and each email step still checks the person can be emailed.

Read and write key · accepts Idempotency-Key

  • subscriber_ids array of string
  • email string
curl -X POST https://app.noosletter.com/v1/automations/ID/run \
  -H "Authorization: Bearer $NOOS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"email":"ada@example.com"}'

Webhook endpoints

GET /v1/webhooks

List this key's webhooks

Only the endpoints this key added. Ones added in Settings → Developers aren't listed.

Read, or read and write key

curl -X GET https://app.noosletter.com/v1/webhooks \
  -H "Authorization: Bearer $NOOS_API_KEY"

POST /v1/webhooks

Add a webhook

Sends the chosen events to an https address, signed like every Noosletter webhook. Zapier calls this when a Zap is turned on. A read key can add one: webhooks carry nothing a read key can't already read. API keys can add up to 100 between them, separate from the 10 in Settings. Revoking the key removes every webhook it added.

Read, or read and write key · accepts Idempotency-Key

  • url string, required. An https:// address on the public internet
  • events array of string, required
curl -X POST https://app.noosletter.com/v1/webhooks \
  -H "Authorization: Bearer $NOOS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://hooks.example.com/noosletter","events":["subscriber.created","subscriber.level_changed"]}'

DELETE /v1/webhooks/{id}

Remove a webhook

Only one this key added. Zapier calls this when a Zap is turned off.

Read, or read and write key

curl -X DELETE https://app.noosletter.com/v1/webhooks/ID \
  -H "Authorization: Bearer $NOOS_API_KEY"

Webhooks

Add an https endpoint in Settings → Developers, or with POST /v1/webhooks (see Webhook endpoints), and choose its events. We POST a JSON body like this, signed with the endpoint's secret:

{
  "id": "delivery id",
  "type": "subscriber.created",
  "created_at": "2026-09-26T09:14:03.120Z",
  "data": {
    "subscriber": { "id": "…", "email": "ada@example.com", "first_name": "Ada", "last_name": null },
    "newsletter": { "id": "…", "name": "The Friday Letter" },
    "source": "hosted_form",
    "occurred_at": "2026-09-26T09:14:03.100Z"
  }
}

Events:

  • subscriber.created
  • subscription.created
  • subscriber.resubscribed
  • subscriber.unsubscribed
  • email.scheduled
  • email.sent
  • form.submitted
  • subscriber.invited
  • subscriber.level_changed
  • referral.joined
  • comment.created

subscriber.created is a new subscriber: someone with no active subscription to any of your newsletters before. Someone already subscribed who joins another of your newsletters is subscription.created.

form.submitted fires on every accepted submission. Before confirmation, for anyone who isn't already a signed-in reader. With a form object (id, name) and a submission object (email, first_name, last_name, utm_source, utm_medium, utm_campaign) in place of subscriber/newsletter.

subscriber.invited fires when someone is invited (someone signing themselves up counts only once they confirm, assubscriber.created), with an invite object (email, method, expires_at). subscriber.level_changed carries level and previous_level (number and the newsletter's name for it) and a direction of up or down. referral.joined names the subscriber whose invite link was used and the referred person. comment.created carries the comment, the email it's on, and the subscriber, or null if the commenter isn't one of yours.

There are no open or click events, and no payload says who opened or clicked. Answer with any 2xx within 10 seconds. Anything else is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 12 h, 24 h, then marked failed; you can retry it from the delivery log. Redirects aren't followed.

Check each request came from us with the Noosletter-Signature header (t= time, v1= HMAC-SHA256 of <t>.<raw body>):

import crypto from "node:crypto";

// header: the Noosletter-Signature header, e.g. "t=1760000000,v1=5f2c…"
// body: the raw request body, exactly as received
export function isFromNoosletter(secret, body, header) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false; // older than 5 minutes
  const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${body}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 ?? ""));
}

Command line

noos wraps the API for scripts and quick jobs. It has no logic of its own.

npm install -g @noosletter/cli
export NOOS_API_KEY=noos_…

noos newsletters
noos subscribers add ada@example.com --newsletter NEWSLETTER_ID --consent "Asked at our event"
noos emails create --subject "This week" --file issue.md
noos emails send EMAIL_ID --yes
noos stats EMAIL_ID
Start your Noosletter