What this is about
All templates are in the database: the default per email and language, plus the wording of individual tenants. They can be changed at runtime, without a new delivery. Because an email is sent in the platform’s name, it is precisely defined who may change what.
The calls
| Call | Does |
|---|---|
GET /cias/notification/templates | all templates you may see |
POST /cias/notification/templates | save a template: create or replace |
POST /cias/notification/templates/{id}/reset | reset a default to the shipped text |
DELETE /cias/notification/templates/{id} | delete a tenant’s wording. The tenant then gets the default again |
An editor sees every template in the list, a tenant administrator only their tenant’s wording. The calls exist only if codamai.cias.notification.enabled, persistence: jpa and rest are switched on. Otherwise they answer 404, as if they did not exist.
Save a template
POST /cias/notification/templates
Authorization: Bearer <token of Anna, tenant-admin in nordbau>
{
"key": "WELCOME",
"locale": "de",
"subject": "Willkommen bei Nordbau",
"body": "Guten Tag,\n\nIhr Zugang ist bereit: ${loginUrl}\n\nIhr Nordbau-Team",
"html": false
}HTTP 200
{
"id": "7d2c…",
"key": "WELCOME",
"tenantKey": "nordbau",
"locale": "de",
"subject": "Willkommen bei Nordbau",
"body": "Guten Tag, …",
"html": false,
"updatedBy": "5c9e…",
"updatedAt": "2026-10-01T10:15:00Z",
"origin": "EDITED",
"isDefault": false
}| Field | Meaning |
|---|---|
id | missing: create, or replace the template with the same features. Set: replace exactly this template |
key | which email, see Which emails exist |
tenantKey | for which tenant. Only an editor can set it. Empty means: the default. For everyone else CIAS takes the tenant from the token and discards the value |
locale | the language, for example de or en |
subject, body | subject and text, with placeholders like ${loginUrl} |
html | whether the text is HTML. Then CIAS also sends a plain-text version |
updatedBy, updatedAt | who changed it last and when. The person comes from the token. Empty for a shipped default |
origin | PACKAGED while it is the shipped text, EDITED once somebody changed it |
isDefault | whether this is the default, the template without a tenant |
A template is identified by email, tenant and language. Without a tenant there is exactly one per email and language: saving without tenantKey changes the default, it never creates a second one. Saving twice with the same features likewise results in one template.
The check
-
CIASauthenticatedDoes the request have a token, and does it name the person?↳ no 403
cias.notification.template-forbidden -
CIASeditor?yes → may save any template, the tenant comes from
tenantKey -
CIAStenant administratorDoes the caller have the tenant administrator role, and does their token name a tenant?↳ no 403
cias.notification.template-forbidden -
CIASapproved kindIs the email on the list that tenants may change?↳ no 422
cias.notification.template-rejected -
CIASown templateWhen replacing by
id: does the template belong to the own tenant, and is it not a default?↳ no 403cias.notification.template-forbidden -
CIAScan be generatedCIAS generates the template once as a trial, with empty values for all placeholders. Does that work?↳ no 422
cias.notification.template-rejected, with the first line of the error message - Template saved
Who may do what, per installation
Which roles are editors is set by codamai.cias.notification.editor-roles, or the environment variable CIAS_NOTIFICATION_EDITOR_ROLES. The default is platform-admin,mail-template-admin. Which emails tenants may change is decided by each installation. There is deliberately no default for that: an installation that has not asked itself the question does not start.
| Installation | Editors | Tenant role | Emails tenants may change |
|---|---|---|---|
| standalone CIAS | from CIAS_NOTIFICATION_EDITOR_ROLES, default platform-admin, mail-template-admin | tenant-admin | MEMBERSHIP_INVITATION, INVITATION, WELCOME |
| embedded in the hub backend | the installation’s platform administrator roles, plus the same setting | tenant-admin | none |
mail-template-admin is a realm role, see The platform’s realm roles. It reaches the wording of every tenant and is therefore never granted within a tenant.
The list for tenants is an allow list, not a block list. New emails are therefore blocked for tenants at first. Why these in particular are not approved:
VERIFY_EMAILandPASSWORD_SETUPcarry a link that opens an account. Whoever can reword them can write a convincing phishing email from the platform’s sender.ALREADY_REGISTEREDmay go to someone from an entirely different tenant.APPROVAL_PENDINGandREJECTEDword a decision. Whoever decides should not rewrite it afterwards.
The variants
When: The token carries platform-admin or mail-template-admin.
May save any template: the default (tenantKey empty) or the wording of any tenant. Is not bound by the allow list. Sees all templates in the list.
Result: allowed
When: Anna is tenant-admin in nordbau and saves WELCOME.
CIAS saves the template as the wording of nordbau, whatever is in tenantKey. Anna sees only the wording of nordbau in the list.
Result: allowed, if WELCOME is approved
When: Anna wants to change VERIFY_EMAIL.
The email is not on the allow list. The answer names the reason, so Anna knows what is going on.
Result: 422 cias.notification.template-rejected
When: Anna sends the id of a default or of another tenant's wording.
Only editors change defaults.
Result: 403 cias.notification.template-forbidden
When: The text contains, for example, Hallo ${name without a closing brace, or a blocked command like ?new.
CIAS refuses to save and names the spot. Nothing is saved.
Result: 422 cias.notification.template-rejected
When: POST /cias/notification/templates/{id}/reset by an editor
The default gets the shipped text again and counts as PACKAGED. So with the next release it follows automatically again. For an email CIAS ships no text for, there is nothing to reset.
Result: 200 with the template, or 422 cias.notification.template-rejected
When: DELETE /cias/notification/templates/{id}
The same rules as for saving. Afterwards the tenant gets the default again. A default cannot be deleted, only reset. A template you may not see does not exist for you: 404, not 403. That way nobody can count which templates other customers have.
Result: 204, 404 cias.notification.template-not-found, or 422 for a default
What a template can do
Templates are written in FreeMarker, a template language. You can insert placeholders (${loginUrl}) and write simple conditions:
<#if passwordUrl?has_content>
Vergeben Sie jetzt Ihr Passwort: ${passwordUrl}
<#else>
Melden Sie sich an: ${loginUrl}
</#if>
Nothing more. Because people other than the developers write templates, FreeMarker is locked down: a template cannot create classes, cannot call methods and cannot reach the objects behind the values. It only sees the values of the context, plus ${application} and ${flow} in every email.
When saving, CIAS generates the template once as a trial, with the same locked-down setup and empty values for all placeholders. A template that cannot be generated this way is refused. Still check with a real email that the text arrives the way you want: placeholders with values sometimes behave differently from empty ones.
Errors at a glance
| Response | When |
|---|---|
400 cias.notification.invalid-payload | required field missing, field too long (subject up to 512, text up to 65535 characters), invalid email key |
403 cias.notification.template-forbidden | not authenticated, no suitable role, someone else’s template, tenant administrator tries to reset |
404 cias.notification.template-not-found | id unknown or invisible to you |
| 404 without a body | the calls are switched off in this installation |
422 cias.notification.template-rejected | tenants may not change this email, template cannot be generated, deleting a default, nothing to reset to |