Quickstart
Two things happen when you install imio.emailkit: Plone's stock transactional mails start looking like they were designed, and you get an API for sending your own. This page covers both, in that order, because the first one needs no code from you at all.
Install the add-on
pip install imio.emailkit
Then install it in Site Setup, or apply the imio.emailkit:default GenericSetup profile.
Nothing here needs Node.js. Node compiles templates on a developer machine or in CI; the released egg carries the compiled output. See Architecture.
Check that it worked
Trigger a password reset for any user. The mail that arrives is the kit's, not Plone's: an iMio header, a real call-to-action button, an inlined stylesheet and a hidden preheader line.
To look at every template at once, @@emailkit-preview renders all of them against their committed fixtures, with a language switcher and a send-test button. See Preview & send-test.
Send a mail from your own code
The Email builder is the normal path. It resolves recipients, groups them by language, renders once per language, and hands each message to MailHost inside the current transaction.
from imio.emailkit import Email
Email("imio.emailkit:notification").to(member).to("greffe@commune.be").with_context(
title="Budget 2026",
intro="The item you follow has changed state.",
cta_url=item.absolute_url(),
).send()
That is a complete send. There is no message assembly to write, no From header to set, and no MailHost call to make.
imio.emailkit:notification is the worked example the package ships. Use it
to get an end-to-end send working before you author a template of your own.
What just happened
- Name
Subject- Description
Taken from the template's registration as an i18n msgid and translated per language group — not passed by you.
.subject()overrides it when you need to.
- Name
Recipients- Description
memberand the literal address were both adapted toIEmailRecipient. An unresolvable recipient raisesRecipientErrorat.send(), never a silent drop.
- Name
Language- Description
One message per distinct recipient language. An FR member and an NL member get two different mails from this one call.
- Name
Delivery- Description
Queued through
IMailHost. If the transaction aborts, nothing is sent.
If you only need the HTML
render() is the layer underneath, and it is a pure function — no recipients, no sending, nothing but the template, your context and the registry:
from imio.emailkit import render
html, text = render(
"imio.emailkit:notification",
context={"title": title, "intro": intro, "cta_url": url},
language="fr",
)
Use it for previews, tests, or when something other than MailHost will deliver the result.
Where to go next
- Architecture — the build-time / runtime seam, and why it is shaped that way.
- Installation & profiles —
:baseversus:default, and how to opt out. - Email builder — every method, and what
.send()guarantees. - Shipping templates — get your own add-on's templates discovered.
- Migrating a mail — you already build HTML bodies and want the shell around them.