Subscription management
The unsubscribe link in every email opens a page Mimeo hosts, on your domain, that asks for one confirming click. Its text is yours to change, or it can send people on to a page of yours after they confirm — and these public, token-authenticated endpoints are what a page of yours would call to offer resubscribe. This page has a complete working example.
Mimeo's unsubscribe page
Out of the box, {{ unsubscribe_url }} in your footer resolves
per recipient to
https://mimeo.yourdomain.com/unsubscribe/<token>
on your connected sending domain (or your Mimeo's own host until one is connected). Opening the link never unsubscribes anyone: the page asks for confirmation, names the address and the sender, and the person is out when they press its Unsubscribe button — one click on the page, and only that click. Once unsubscribed, the page says so and offers a Resubscribe button for the accidental tap. Resubscribing shows the reverse: "You're subscribed again", with an Unsubscribe button.
- Attributed exactly. The person's record shows unsubscribe · via link, tied to the specific email they clicked from, so per-email unsubscribe counts in Reporting are facts.
- Scanner-safe. Corporate link scanners (Safe Links, Proofpoint and kin) open every URL in an email on the recipient's behalf — including this one. GET and HEAD requests never change subscription state, whatever the user agent claims to be, so a scan can't opt anyone out. Only the page's own POST unsubscribes.
-
Not indexable. The page is
noindex, disallowed inrobots.txt, and the bare/unsubscribepath is a 404 — the token is the whole address. - Always first. Even with a custom URL set (below), the click lands on Mimeo's page first — that's where the unsubscribe happens — so an opt-out never depends on anyone else's server being up.
The one-click header URL (/u/<token>) opened as a plain
link — what an older mail client does with it — also lands here.
Making it yours
The page's text is yours to change under Settings → General →
Unsubscribe Flows, separate from Email footer. The
introduction appears above the unsubscribe form instead of a repeated
large heading. Its default is {email} will stop getting all email
from us. A preview walks through the page's states: status line,
headline, confirmation text, resubscribe
button (its text and color) and the small text under it for the
just-unsubscribed state, and the same set for the state after someone
resubscribes. {email} in the introduction or either confirmation
is safely replaced with the person's address in bold. Every unsubscribe
copy field has a default; blank means
the default. The same fields are the
sending.unsubscribe_page.* settings over the
settings API, MCP and your
definitions repo.
Hosted subscription preferences
Enable Mimeo manage subscriptions page in Unsubscribe Flows to add preferences alongside global unsubscribe. Both hosted settings tabs show In use when enabled; they are not alternatives. The subscriber page then has two tabs, initially labeled Unsubscribe from everything and Manage my subscriptions. You can change both labels and which tab opens first. Each tab has its own introductory text and form: saving preferences never submits an unsubscribe, and resubscribing never applies preference actions.
Toggles start from their On when tag or custom-field conditions. They never auto-save. The customizable Save preferences button stays disabled until at least one toggle differs from its saved state; reverting every change or saving successfully disables it again. Globally unsubscribed people see the management form faded and disabled until they explicitly resubscribe.
On save, every toggle runs its selected on or off
action list, not just toggles that changed. Actions add/remove tags or
set/clear custom fields and must leave each condition matching its
selection, or the whole save rolls back. Configure the labels, conditions,
actions and copy through
sending.subscription_preferences.
The user guide
walks through a weekly-news toggle and the sending rules it needs.
POST /unsubscribe/:token/preferences belongs to the hosted
subscriber form, not the operator API. It submits
selections for every configured toggle ID plus the current
configuration revision; a stale configuration must be reloaded.
Use the settings API to configure the form, or a server-side
event integration
for your own preference workflow. Never put an operator API token in a
subscriber's browser.
Sending people to your own page
Set a Custom unsubscribe URL under Settings → General
→ Unsubscribe Flows (the setting is sending.unsubscribe_page_url,
writable through the settings API, MCP,
and your definitions repo). The footer link still opens Mimeo's page,
and the person still confirms and unsubscribes there — the URL only
changes where they land next. Instead of Mimeo's confirmation they're
sent straight on to your URL, so it can be a plain "you're out" page with
no logic at all.
This is a redirect after global unsubscribe, not a replacement selective-preference handler. Saving a custom URL in Settings disables hosted preferences but retains their definitions. Enabling hosted preferences clears the custom URL; disabling them retains the definitions too. See the settings API for how to write these settings explicitly through the API.
If you'd like that page to offer resubscribe (or show the address), the person's signed token rides along:
-
Appended as a query parameter —
https://yoursite.com/unsubscribed?token=<token>(joined with&if the URL already has a query). -
Or, if the URL contains
{token}, dropped in there —https://yoursite.com/u/{token}becomeshttps://yoursite.com/u/<token>.
Your page then calls the endpoints below with it. Clear the setting and people stay on Mimeo's page.
Tokens
Every one of these endpoints is authenticated by a signed token:
- Signed server-side, so it can't be forged or guessed.
- Encodes the person and the exact email that brought them there. That's exact-email attribution — when someone unsubscribes, you know which specific message prompted it, not just that they left.
- Never expires.
Non-expiring is intentional. Someone finding an old email in their archive two years from now must still be able to unsubscribe. An expired opt-out link is a complaint waiting to happen.
Endpoints
All of these are public — no bearer token, because they run
in your subscribers' browsers. CORS is open (*) so your page
can call them from any origin you host it on.
Get status
GET /api/v1/subscription/:token
{
"email": "ada@example.com",
"unsubscribed": false,
"unsubscribed_at": null,
"reason": null
}
Call this on page load. Showing people the address they're managing prevents the most common confusion — someone with several addresses not knowing which one they just opted out.
Call it with fetch() (or anything that doesn't ask for
text/html). A browser navigating to this URL is a
person, not a page — email sent before Mimeo's page existed linked its
footer straight here — so that request is redirected to Mimeo's
unsubscribe page instead of showing JSON.
Unsubscribe
POST /api/v1/subscription/:token/unsubscribe
Content-Type: application/json
{ "reason": "too_frequent" }
reason is optional. The call:
- Records the unsubscribe locally with its reason and attribution — which email, which send.
- Stops future sends immediately.
- Pushes the opt-out to your provider where the provider supports it.
- Is idempotent — unsubscribing an already-unsubscribed person succeeds rather than erroring, so a double-click or a retry is harmless.
Returns the same status shape as the GET, reflecting the new state.
Resubscribe
POST /api/v1/subscription/:token/resubscribe
Reverses it. Worth offering on the confirmation screen — accidental unsubscribes are common, and the alternative is that they're gone. Like unsubscribe, it's idempotent and returns the status shape; the example below wires it to a single button.
One-click unsubscribe (RFC 8058)
POST /u/:token
This is the target mail clients POST to when someone uses the unsubscribe button Gmail and Apple Mail show next to the sender name. You never call it yourself. Mimeo handles the POST directly, including with Resend — it does not first go through Resend or wait for a webhook. It always performs a global unsubscribe for a valid token, regardless of the preference configuration. It always returns 200, because a mail client treating an error as "unsubscribe failed" is worse than any failure it could report.
Mimeo sets the List-Unsubscribe headers that point here on
every broadcast and sequence send.
Global unsubscribe and selective preferences are different. The unsubscribe/resubscribe endpoints change the person's global suppression state. Hosted preference toggles only change tags and custom fields. Your audiences, segments and send-time guards must use those values; enabling preferences does not add sending guards.
Build your unsubscribe page
A complete working page, in one file, with no dependencies — for when
a plain landing page isn't enough and you want your own to show the
address and offer resubscribe. It fetches status on load, shows the
address, and wires both buttons. Change BASE to your Mimeo,
host it anywhere, set its URL as your Custom unsubscribe URL, and style it
as your own. It reads the token from ?token=, which is how
Mimeo passes it by default. Because Mimeo has already unsubscribed the
person by the time they arrive, the page opens on its "you're
unsubscribed" state.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Email preferences</title>
</head>
<body>
<main>
<p id="loading">Loading…</p>
<section id="form" hidden>
<h1>Unsubscribe</h1>
<p>You're subscribed as <strong id="email"></strong>.</p>
<label for="reason">Why are you leaving? (optional)</label>
<select id="reason">
<option value="">Prefer not to say</option>
<option value="too_frequent">Too many emails</option>
<option value="not_relevant">Not relevant to me</option>
<option value="never_signed_up">I never signed up</option>
</select>
<button id="unsubscribe" type="button">Unsubscribe me</button>
</section>
<section id="done" hidden>
<h1>You're unsubscribed</h1>
<p>We won't email you again. Changed your mind?</p>
<button id="resubscribe" type="button">Resubscribe</button>
</section>
</main>
<script>
// Your Mimeo — the connected sending domain's link host, or the app host.
const BASE = "https://mimeo.yourdomain.com/api/v1/subscription";
// Mimeo appends ?token=… to your Custom unsubscribe URL. (If you'd rather
// have it in the path, put {token} in the URL and read it from there.)
const token = new URLSearchParams(location.search).get("token");
const el = (id) => document.getElementById(id);
async function call(path, body) {
const res = await fetch(BASE + "/" + token + path, {
method: body === undefined ? "GET" : "POST",
headers: { "Content-Type": "application/json" },
body: body === undefined ? undefined : JSON.stringify(body),
});
if (!res.ok) throw new Error("Request failed: " + res.status);
return res.json();
}
function render(state) {
el("loading").hidden = true;
el("email").textContent = state.email;
el("form").hidden = state.unsubscribed;
el("done").hidden = !state.unsubscribed;
}
el("unsubscribe").addEventListener("click", async () => {
render(await call("/unsubscribe", { reason: el("reason").value }));
});
el("resubscribe").addEventListener("click", async () => {
render(await call("/resubscribe", {}));
});
call("")
.then(render)
.catch(() => {
el("loading").textContent = "That link isn't valid.";
});
</script>
</body>
</html>
Things worth keeping when you restyle it
- Show the email address. People manage several; they need to know which one this is.
- One button, nothing more. Making unsubscribing hard produces spam complaints, which cost far more than the subscriber did. One press should do it — but never unsubscribe on page load: corporate link scanners open every URL in an email, and a GET that mutates opts out people who never clicked (see above).
- Keep the reason optional. A required reason is a barrier; an optional one still gets answered often enough to be useful, and the answers show up on the person's record.
- Offer resubscribe on the confirmation. It costs one button and recovers accidental clicks.
- Handle the invalid-token case. Links get mangled by mail clients; say so plainly rather than showing a blank page.