Newsletter API
The PerspectAPI v2 SDK provides newsletter subscription management with double opt-in, list segmentation, and campaign access.
Quick Start
import { PerspectApiV2Client } from 'perspectapi-ts-sdk';
const client = new PerspectApiV2Client({
baseUrl: 'https://api.example.com',
apiKey: 'your-api-key'
});
// Subscribe a user
const subscription = await client.newsletter.subscribe('my-site', {
email: 'user@example.com',
name: 'John Doe',
double_opt_in: true
});
console.log(subscription.id); // "nl_42"
console.log(subscription.status); // "pending" (awaiting confirmation)
Subscription Flow
1. Subscribe
const sub = await client.newsletter.subscribe('my-site', {
email: 'subscriber@example.com',
name: 'Jane Smith',
list_ids: ['list_weekly_digest', 'list_product_updates'],
frequency: 'weekly',
topics: ['tech', 'business'],
double_opt_in: true, // Default: true
source: 'homepage',
metadata: {
campaign: 'summer-2025'
}
});
Response:
{
"object": "newsletter_subscription",
"id": "nl_42",
"email": "subscriber@example.com",
"name": "Jane Smith",
"status": "pending",
"double_opt_in": true,
"confirmed_at": null,
"source": "homepage",
"frequency": "weekly",
"topics": ["tech", "business"],
"created_at": "2025-04-01T12:00:00Z"
}
When double_opt_in is true (default), the user receives a confirmation email. When false, the subscription is confirmed immediately.
2. Confirm (Double Opt-In)
Confirmation is triggered when the user clicks the link in their email. The link contains a token that your frontend passes to the API:
const confirmed = await client.newsletter.confirm('my-site', 'confirmation-token-here');
console.log(confirmed.status); // "confirmed"
3. Unsubscribe
Unsubscribe by token (from email footer link) or by email address:
// By token (from unsubscribe link)
const unsub = await client.newsletter.unsubscribe('my-site', {
token: 'unsubscribe-token-here',
reason: 'Too many emails'
});
// By email (admin/API use)
const unsub = await client.newsletter.unsubscribe('my-site', {
email: 'subscriber@example.com'
});
Admin API
List Subscriptions
const subs = await client.newsletter.listSubscriptions('my-site', {
limit: 20,
status: 'confirmed'
});
// {
// object: "list",
// data: [{ object: "newsletter_subscription", id: "nl_42", ... }],
// has_more: true,
// url: "/v2/sites/my-site/newsletter/subscriptions"
// }
Get Subscription
const sub = await client.newsletter.getSubscription('my-site', 'nl_42');
List Newsletter Lists
const lists = await client.newsletter.listLists('my-site');
for (const list of lists.data) {
console.log(`${list.name}: ${list.subscriber_count} subscribers`);
}
List Campaigns
const campaigns = await client.newsletter.listCampaigns('my-site', {
status: 'sent'
});
Get Campaign
// By prefixed ID
const campaign = await client.newsletter.getCampaign('my-site', 'camp_7');
// By slug
const campaign = await client.newsletter.getCampaign('my-site', 'summer-newsletter-2025');
Error Handling
import { PerspectV2Error } from 'perspectapi-ts-sdk';
try {
await client.newsletter.subscribe('my-site', { email: 'user@test.com' });
} catch (err) {
if (err instanceof PerspectV2Error) {
switch (err.code) {
case 'invalid_list_ids':
console.error('Unknown list IDs:', err.param);
break;
case 'rate_limit_exceeded':
console.error('Too many requests');
break;
default:
console.error(err.message);
}
}
}
Framework Examples
React Subscribe Form
import { PerspectApiV2Client } from 'perspectapi-ts-sdk';
const client = new PerspectApiV2Client({
baseUrl: import.meta.env.VITE_API_URL,
apiKey: import.meta.env.VITE_API_KEY
});
function NewsletterForm() {
const [email, setEmail] = useState('');
const [status, setStatus] = useState<'idle' | 'pending' | 'confirmed' | 'error'>('idle');
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
try {
const sub = await client.newsletter.subscribe('my-site', {
email,
double_opt_in: true
});
setStatus(sub.status === 'pending' ? 'pending' : 'confirmed');
} catch {
setStatus('error');
}
}
if (status === 'pending') return <p>Check your email to confirm.</p>;
if (status === 'confirmed') return <p>Subscribed!</p>;
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
placeholder="your@email.com"
required
/>
<button type="submit">Subscribe</button>
</form>
);
}