Email templates: rewrite every email your organization sends
Every email Forumulate sends on your organization's behalf is a template you can rewrite: member invitations, notifications (replies, mentions, moderation notices and the rest), digests and the weekly summary, contact and form alerts, replies from the contact and careers inboxes, status-page alerts, newsletter confirmations, new-post emails, broadcasts and roadmap updates.
You can change each one for the whole organization or for one space, in one language or all of them, and every one is drawn inside your brand: your logo, colours, type, footer and links.
Open it from Advanced → Emails in the console (/o/<org>/emails).
What you can change
Each email has:
Subject, the line in the inbox.
Preheader, the grey line an inbox shows after the subject. Leave it empty for none.
Blocks, the message itself, built from a small set that every mail client can draw:
Block | What it is |
|---|---|
Heading | A title, three sizes. |
Text | A paragraph. Line breaks are kept. Can be set smaller and quieter. |
Button | A link drawn as a button, with the address spelled out underneath unless you turn it off. |
Image | An image, optionally linked, up to 600px wide. |
Divider | A rule. |
Space | Small, medium or large vertical space. |
Small print | Quiet text inside the message. |
Text, headings and small print accept two pieces of markup: **bold** and [label](https://address).
The list of blocks is deliberately short. An email is read in Outlook on Windows and in Gmail's app as well as in a browser, and each of these blocks has a rendering (tables and inline styles) that survives all of them.
Variables
Write {{variable}} to put a value in. Each email lists the variables it can use, with an example of each, beside the editor: click one to insert it where your cursor is. For example, the contact-inbox reply can use {{recipient.name}}, {{reply.body}} and {{original.subject}}.
Every email can also use:
Variable | Value |
|---|---|
| Your organization's name. |
| The space the email is about, when it is about one. |
| The address the email is going to. |
| The one-click unsubscribe address, on mail that has one. |
Rules that keep a template safe:
A misspelled variable is refused when you save. The save answers
422and names the variable and where it is, for exampleUnknown variable {{recipient.nmae}} in blocks[0].text. A template that saves is a template every send can fill.Every value is escaped. A name, a topic title or a message a stranger typed into your contact form is always shown as text. There is no syntax that turns escaping off, so a value can never become a link, bold text or HTML. Markup only ever comes from the template you wrote.
A block whose variables all come out empty is left out. "Sent from:
{{submission.pageUrl}}" disappears when the browser did not say which page a form was sent from, rather than arriving as "Sent from:".A button or link whose address does not work is left out, rather than sent broken. Addresses must be absolute
http(s)://ormailto:.
Spaces and languages
A version is saved for a scope (the whole organization, or one space) and a language (one of English, Spanish, French, German, Portuguese and Arabic, or every language).
When an email is sent, the version used is chosen in this order:
this space's version in the recipient's language
this space's version for every language
the organization's version in the recipient's language
the organization's version for every language
the default
The space comes first, then the language. A space that rewrote its welcome email did so for a reason the organization-wide French translation knows nothing about. The recipient's language is the one they chose for Forumulate. When it is not known (a newsletter subscriber with no account, for example), English is used, which falls back to the every-language version.
Reset to default deletes the version for the scope and language you are looking at. The email then falls back down the list above.
Preview and test sends
The preview beside the editor is drawn by the same code that sends the email, using your brand and the example value of each variable. Switch between desktop and phone widths, light and dark, and the HTML and plain-text parts.
Send me a test emails the draft you are looking at, saved or not, to your own address through your organization's mail provider. You get five every ten minutes. Tests only ever go to the address of the person sending them.
Every email has a plain-text part as well as the HTML one. It is made from the same blocks, so you never write it separately.
The sending address
Mail goes out from Forumulate's address by default. An organization can send from its own instead, and carry its brand settings with it. One read returns both, so a settings screen needs a single request:
GET /orgs/{orgId}/email-settings{
"emailFrom": "Acme Help <help@acme.example>",
"branding": { "footer": "Acme Ltd, 1 High Street", "subjectPrefix": "[Acme] ", "hideBranding": false }
}emailFrom is null until you set one. It needs org.read. To change it, use PATCH /orgs/{orgId}/email-from { "emailFrom": "..." } (send null to clear; up to 320 characters), which needs org.manage_settings. The branding object is the same one …/email-branding reads and writes, described in the next section.
The brand
The Brand tab sets what every email is drawn in:
Header: a logo (an image address, or one from your library) with its width and description, or a name shown when there is no logo.
Colours: buttons and links, button text, background, message card, text and quiet text, plus a dark set. Mail apps that support dark mode use the dark set, and the emails declare
color-scheme: light dark. Turn dark mode off and the emails ask to stay light.Type: a font stack that needs nothing downloaded (system, Helvetica, Arial, Verdana, Georgia and others). You can add a web font in front of it, with its stylesheet address, for the mail apps that load one. Outlook on Windows never does, which is why the stack underneath matters.
Footer: footer text, a postal address (bulk mail needs one under CAN-SPAM), footer links and social links.
Subject prefix: prepended to every subject, once.
Hide the "Sent with Forumulate" line.
A message with an unsubscribe address always carries an Unsubscribe link in its footer, whatever the template says. You cannot remove it, because mail providers require it of bulk mail.
Timing
A saved version shows up in the preview at once and in delivered mail within a minute. The sending worker caches each organization's templates for a minute so that a newsletter to thousands of subscribers reads them once.
API
Reading needs org.read. Every change, preview and test send needs the email.manage capability, which Owners and Admins hold, or the email:manage token scope. The brand is at /orgs/{orgId}/email-branding and needs org.manage_settings to change.
Method and path | What it does |
|---|---|
| Every customizable email, with the versions saved for each. |
| One email: its variables, its default and every saved version. |
| Save a version: |
| Reset a version. |
| Render: |
| Send yourself a test: |
| Read or change the brand. |
Keys look like "member.invite", "contact.reply", "newsletter.issue" and "notification.topic.reply". GET …/email-templates lists them all.
curl -X PUT "$API/orgs/$ORG/email-templates/member.invite" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{
"locale": "fr",
"subject": "Rejoignez {{org.name}}",
"preheader": "{{inviter.name}} vous invite",
"blocks": [
{ "type": "heading", "text": "Bienvenue chez {{org.name}}" },
{ "type": "text", "text": "**{{inviter.name}}** vous invite à nous rejoindre." },
{ "type": "button", "label": "Accepter", "url": "{{link}}" }
]
}'Account emails about a person's own Forumulate sign-in (address verification and password reset) are sent by Forumulate, from our domain, and are not customizable. The API answers 422 if you try.
Bounces, replies and unsubscribes
Four addresses receive mail traffic rather than send it. Only the first one needs anything from you.
Bounces from your own mail provider. When the organization sends through its own provider (Brevo, SendGrid, Postmark, Mailgun or Resend, connected with PUT /orgs/{orgId}/mail/provider), GET /orgs/{orgId}/mail/provider returns a webhookUrl. Paste it into the provider's event webhook setting. It is POST /mail/webhooks/{provider}/{orgId}/{token}, where the token is signed for your organization, so a wrong one is a 401 and an unknown provider a 404. A hard bounce or a spam complaint stops your organization's mail to that address (it shows in GET /orgs/{orgId}/mail/suppressions, and DELETE on the same address clears it); a soft bounce only counts toward the bounce score. What your provider reports applies to your organization alone: it never stops mail that other organizations or the platform itself send to that address. An event type we do not act on answers { "ingested": 0 } rather than an error, because providers switch off webhooks that keep failing.
Bounces from our own sending. Mail that goes out through the platform's account reports its bounces and complaints to POST /mail/events, signed with our own shared secret. It feeds the platform-wide suppression list, which stops all mail to an address. You configure nothing.
Replies by email. The reply addresses in notification emails come back to POST /email/inbound, posted by our inbound mail provider with our shared secret, so there is nothing to set up. Mail to a reply+… address becomes a reply on that thread and a post+… address starts a new topic; replies to private messages and group inboxes land in their conversations, and a status page's status+… address opens or updates an incident. Up to 20 attachments are uploaded and attached to the post.
Unsubscribing. Every notification email carries a signed unsubscribe link, and the same link is its RFC 8058 one-click List-Unsubscribe target. Mail clients POST /mail/unsubscribe?token=… on their own; a person who clicks the link gets GET /mail/unsubscribe?token=…, a short confirmation page. The token says what to stop: email turns email notifications off for that member in this organization, and summary stops only the weekly community summary. An invalid or expired token stops nothing (the page answers 400). The member can turn email back on in their notification settings.
In the API
Every operation below is also reachable from the other surfaces without a dedicated command. forumulate api <Operation> calls it from the CLI, the JavaScript SDK has a typed method for each one (the last column names it; the Python and Go clients are generated from the same spec), and an agent reaches the organization-scoped ones through the MCP call_api tool. Where a CLI command or MCP tool exists too, the last column names it. Operation names link to the API reference.
Email Templates
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| List email templates in an organization |
| |
| Get the email template |
| |
| Replace the email template |
| |
| Delete the email template |
| |
| Preview the email template |
| |
| Test the email template |
|
Mail Admin
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| List suppressions in an organization |
| |
| Create a suppression in an organization |
| |
| Delete suppressions in an organization |
| |
| Get the log in an organization (mail) |
| |
| Get the suppression in an organization |
|
Mail Provider
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| Get the provider in an organization (mail) |
| |
| Replace the provider in an organization (mail) |
| |
| Delete the provider in an organization (mail) |
| |
| Test the provider (mail) |
|
Org email branding
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| Get the email branding in an organization |
| |
| Update the email branding in an organization |
|
Org email settings
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| List email settings in an organization |
|
Push
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| Get the vapid public key |
| |
| Get the status |
| |
| Subscribe the push |
| |
| Unsubscribe the push |
|