Email Preferences
Token-based email preference center — allow subscribers to manage their email preferences without login
The preferences API lets your subscribers update their own email preferences without logging in. Access is controlled by a signed token embedded in every unsubscribe and preference link MisarMail generates.
This is a token-based API — no API key or session cookie is required. The subscriber authenticates via a short-lived signed JWT included in outbound email links.
How the token works
Every email sent through MisarMail includes a unique, subscriber-specific signed JWT in the unsubscribe link:
https://mail.misar.io/preferences?token=eyJhbGciOiJIUzI1NiJ9...
The token encodes the subscriber's contact ID and expires after 7 days. An expired or tampered token returns 401.
Get preferences
GET /api/preferences?token=:token
Returns the subscriber's current category preferences and email frequency.
Response
{
"success": true,
"email": "u***@example.com",
"preferences": {
"marketing": true,
"productUpdates": true,
"newsletters": false,
"transactional": true,
"frequency": "weekly"
}
}The email field is masked for privacy — subscribers see enough to confirm it is their address. If the subscriber has never visited the preference center before, all categories default to true and frequency defaults to "immediate".
Update preferences
PUT /api/preferences
Content-Type: application/json
Request body
| Field | Type | Required | Description |
|---|---|---|---|
token | string | Yes | The signed JWT from the preference link |
preferences.marketing | boolean | No | Marketing and promotional emails |
preferences.productUpdates | boolean | No | Product news and feature announcements |
preferences.newsletters | boolean | No | Newsletter subscriptions |
preferences.frequency | string | No | immediate | daily | weekly | monthly | never |
transactional emails (password reset, receipts, security alerts) cannot be disabled. The field is read-only and always returned as true.
Request example
{
"token": "eyJhbGciOiJIUzI1NiJ9...",
"preferences": {
"marketing": false,
"newsletters": false,
"frequency": "weekly"
}
}Response
{
"success": true,
"message": "Preferences updated",
"preferences": {
"marketing": false,
"productUpdates": true,
"newsletters": false,
"transactional": true,
"frequency": "weekly"
}
}Embedding a preferences link
MisarMail automatically adds unsubscribe and preference links when you send campaigns. For custom HTML emails, inject the link using the {{preferences_url}} merge tag:
<a href="{{preferences_url}}">Manage email preferences</a>
Generating tokens server-side
If you are building a custom preference center, you can generate a token via the contacts API and redirect the user to your own page:
curl -X POST https://api.misar.io/mail/v1/contacts/em_abc123/preference-token \
-H "Authorization: Bearer msk_..."{ "token": "eyJhbGciOiJIUzI1NiJ9...", "expires_at": "2025-06-21T10:00:00Z" }Pass the token to your preference center page, then call PUT /api/preferences with it directly.
Security
- Email addresses are masked in GET responses (
u***@example.com) to prevent email harvesting via leaked tokens - Tokens are signed with HMAC-SHA256 and include an expiry — they cannot be forged or extended
- The endpoint is rate limited by IP to prevent token enumeration attacks
- Invalid or expired tokens return
401 Unauthorizedwith no further detail
Example
# Get preferences
curl "https://api.misar.io/mail/api/preferences?token=eyJhbGciOiJIUzI1NiJ9..."
# Update preferences
curl -X PUT https://api.misar.io/mail/api/preferences \
-H "Content-Type: application/json" \
-d '{
"token": "eyJhbGciOiJIUzI1NiJ9...",
"preferences": { "marketing": false, "frequency": "weekly" }
}'// Typically called from your preference center page
const token = new URLSearchParams(window.location.search).get('token');
// Fetch current preferences
const res = await fetch(`/api/preferences?token=${token}`);
const { preferences } = await res.json();
// Save changes
await fetch('/api/preferences', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token, preferences: { marketing: false } }),
});