One template, every channel: how Notavia's rendering pipeline works
Liquid templates, per-channel rendering, idempotency keys, and a delivery log you can actually debug — the engineering behind Notavia's core loop.
The hardest part of building a multi-channel notification service is not the delivery. Connecting to an SMTP relay or a Vonage API is a solved problem. The hard part is the template — and specifically, keeping a single source of truth for your notification copy while producing output that is appropriate for each channel.
This post describes how Notavia solves that, and some of the engineering decisions behind idempotency and observability.
Liquid as the template language
We chose Liquid for templates, rendered by Fluid, its .NET implementation. It is a widely used, logic-capable templating language with a clean syntax that reads well whether you are writing a full HTML email body or a 160-character SMS message.
A Notavia template lives in the console and groups bodies by channel and locale. Email uses a subject, HTML, and optional plain-text body; SMS uses text; in-app uses a title and HTML body; Slack uses mrkdwn text; Teams uses Adaptive Card JSON; and Discord accepts text or embed JSON. When you trigger a send, Notavia resolves the requested channel and locale, renders that body, and dispatches it.
{{ recipient.first_name }}, your invoice for {{ data.amount }} is ready.
The same Liquid expression resolves in both the email subject and the SMS body. Channel-specific bodies let you use HTML, plain text, mrkdwn, or structured JSON without leaking one channel's format into another.
Runtime editability and tenant overrides
Templates are deployed as part of your application configuration, but they are not locked to your deployment pipeline. Workspace administrators can edit template copy through the Notavia console and publish changes instantly — no code deployment required.
This matters for teams that serve multiple business customers. The default template is set by the product developer; individual tenants can override the copy for their branded communications. Notavia keeps a version history so you can roll back if a change produces unexpected output.
Idempotency keys
Every send request accepts an optional idempotency_key. Notavia enforces that key within the current environment: repeating the same request returns the original result without dispatching a second message, while reusing the key for a different request is rejected.
This is not a nice-to-have. It is essential for any system that retries on failure. Without it, a transient network error between your application and the Notavia API can result in a duplicate notification — the invoice paid email sent twice, the security alert SMS appearing at midnight twice. With it, your retry logic becomes safe to run without defensive guards on your side.
Notavia does not currently expose a configurable expiry window for notification idempotency keys. Choose a stable key for the business action you are protecting from duplicate delivery.
Webhooks for delivery outcomes
Notavia emits signed webhooks for implemented notification outcomes, including notification.sent, notification.failed, notification.delivered, notification.bounced, notification.complained, and notification.suppressed. In-app notifications can also emit notification.read. Available outcomes depend on the channel and provider. Email opens and clicks are recorded as tracking events in the delivery log; they are not currently emitted as customer webhooks.
Each webhook uses a signed envelope containing an event ID, event type, creation timestamp, and notification response data. The signature lets your endpoint verify that the payload came from Notavia and was not modified in transit.
If your endpoint was down, you can retry a failed webhook delivery from the console without re-triggering the original notification.
The delivery log
Every send produces a delivery record visible in the console. You can filter by template, by recipient, by channel, by status, and by time range. Each record links to the rendered output, the provider response, and the full webhook history.
The log is not just a debugging tool. It is the audit trail that answers questions from enterprise customers, GDPR regulators, and your own on-call engineer at 3am: "Did this message actually send? What did it say? When did it arrive?"
Building observability into the core delivery loop — rather than bolting it on later — is one of the core design principles of Notavia. Every call is traced. Every error is grouped and surfaced in the console. You should never have to grep your own application logs to understand what a notification sent two days ago actually contained.