Skip to main content

Documentation Index

Fetch the complete documentation index at: https://86d.app/docs/llms.txt

Use this file to discover all available pages before exploring further.

The newsletter module manages your email subscriber database. It handles the full subscriber lifecycle (subscribe, unsubscribe, resubscribe) with tagging and source attribution. The module does not send emails itself; connect it to an email provider like Resend by listening to the newsletter.subscribed event and using RESEND_API_KEY in your integration. Source: modules/newsletter · npm: @86d-app/newsletter

Installation

npm install @86d-app/newsletter
Add the module to your store configuration:
import newsletter from "@86d-app/newsletter";
import { createModuleClient } from "@86d-app/core";

const client = createModuleClient([
  newsletter({
    allowResubscribe: "true",
  }),
]);

Configuration

allowResubscribe
string
default:"\"true\""
When "true", previously unsubscribed (or bounced) addresses can resubscribe. Set to "false" to prevent resubscription; calls to POST /newsletter/subscribe for an unsubscribed address return the subscriber unchanged.

Subscriber lifecycle

subscribe()      → active
unsubscribe()    → unsubscribed  (sets unsubscribedAt timestamp)
resubscribe()    → active        (clears unsubscribedAt, preserves original subscribedAt)
subscribe() is idempotent: calling it for an already-active address returns the existing subscriber unchanged. For an unsubscribed or bounced address, it reactivates the record while preserving the original subscribedAt date.

Store endpoints

MethodPathDescription
POST/newsletter/subscribeSubscribe an email address
POST/newsletter/unsubscribeUnsubscribe an email address

Subscribe (POST /newsletter/subscribe)

{
  "email": "jane@example.com",
  "firstName": "Jane",
  "lastName": "Doe",
  "source": "footer-form",
  "tags": ["launch-announcement"]
}
All fields except email are optional. Use source to track where signups originate (e.g., "footer-form", "checkout-upsell", "product-page"). Tags are stored as a JSON array and can be used to filter subscribers in the admin.

Unsubscribe (POST /newsletter/unsubscribe)

{
  "email": "jane@example.com"
}
Sets the subscriber’s status to unsubscribed and records an unsubscribedAt timestamp.

Admin endpoints

MethodPathDescription
GET/admin/newsletterList subscribers (filter: status, tag; paginate: page, limit)
DELETE/admin/newsletter/:id/deletePermanently delete a subscriber

Query parameters for GET /admin/newsletter

ParamTypeDefaultDescription
statusstringFilter by active, unsubscribed, or bounced
tagstringFilter by a specific tag
pagenumber1Page number (1-indexed)
limitnumber50Results per page (max 100)

Events

EventTriggerPayload
newsletter.subscribedNew subscriber added or reactivatedsubscriberId, email, source
newsletter.unsubscribedSubscriber opts outsubscriberId, email
newsletter.campaign.sentCampaign sent to subscriber listcampaignId, subject, recipientCount
Listen to newsletter.subscribed to forward new signups to an external email provider:
module.on("newsletter.subscribed", async ({ email, source }) => {
  await resend.contacts.create({
    email,
    audienceId: process.env.RESEND_AUDIENCE_ID,
  });
});

Store components

Use these components in your MDX template files.

NewsletterForm

A full email subscription form with optional name fields and customizable copy.
showName
boolean
default:"false"
Show first and last name input fields alongside the email field.
source
string
Attribution source recorded on the subscriber (e.g., "footer-form").
title
string
default:"\"Subscribe to our newsletter\""
Form heading text.
description
string
default:"\"Get the latest updates...\""
Descriptive text displayed below the heading.
compact
boolean
default:"false"
Render the form in a compact inline layout.
<NewsletterForm
  showName={true}
  source="footer"
  title="Stay in the loop"
  compact={true}
/>

NewsletterInline

Inline newsletter signup intended for embedding in blog posts or product pages. Accepts the same props as NewsletterForm. Use compact={true} for inline placement.
<NewsletterInline compact={true} source="product-page" />

NewsletterUnsubscribe

A self-service unsubscribe form for use on your unsubscribe page, typically linked from email footers.
<NewsletterUnsubscribe />

Types

type SubscriberStatus = "active" | "unsubscribed" | "bounced";

interface Subscriber {
  id: string;
  email: string;
  firstName?: string;
  lastName?: string;
  status: SubscriberStatus;
  source?: string;
  tags: string[];
  metadata: Record<string, unknown>;
  subscribedAt: Date;
  unsubscribedAt?: Date;
  createdAt: Date;
  updatedAt: Date;
}