Templates
HTML and plain text. Variables. Locales. Versions. Approvals. Previewing.
Customer communication infrastructure
Send it. Receive it. Find it. Prove it.
Send transactional notifications, honour customer preferences, render approved templates and prove exactly what happened — without building the infrastructure around it.
API · SDK · Webhooks · Managed delivery · Preferences widget
await notifhq.notify("commerce.order.shipped", { recipient: "cus_1284", order_id: "ORD-9218", tracking_url: "https://…", });
You should not have to build this again
The first notification takes an afternoon. Then comes everything around it.
HTML and plain text. Variables. Locales. Versions. Approvals. Previewing.
Topics. Channels. Opt-outs. Consent with a lawful basis. Quiet hours.
Queues. Retries. Idempotency. Suppression. Delivery states that mean different things.
Logs. Timelines. Webhooks. Answering “did they get it?” without reading a provider's logs.
Classification. Minimisation. Redaction. Retention. Evidence an auditor will accept.
Knowing that a template variable exists before the message goes out, not after.
NotifHQ packages these behind one communication layer.
More than a send endpoint
Your application describes what happened. NotifHQ decides how that communication is produced, whether it is permitted, how it is rendered, where it goes and what is recorded.
Your product emits
NotifHQ owns
Reaches people on
3 channels available now; 6 declared across the roadmap.
Typed notifications
Define a communication once. NotifHQ then knows its required payload, its permitted channels, whether a preference may override it, how it de-duplicates and how long it stays worth sending.
Undeclared fields are dropped before anything stores them. A missing required field fails before a policy is even evaluated.
A contract names what it may use. A channel outside that set is refused, not silently substituted.
Most contracts respect an opt-out. A few may not — and those are declared, not decided at the call site.
A late message is worse than none for time-bound content, so a contract states when it stops being worth sending.
58 notification types are described in the dictionary today, across 18 business domains.
The rule everything else follows
Most tools collapse these into one green tick. NotifHQ keeps them apart in the API, the events, the metrics and the timeline — because the moment they merge you can no longer answer the only question that matters when something goes wrong.
You asked. Nothing has been decided, rendered or sent.
A transport accepted it. It has left our control. It has not arrived.
A destination accepted it. A person may still never look at it.
Something was observed — and we say how strong that observation is.
An email open is a tracking-pixel fetch a mail client may make, prefetch, cache or block. We call it an open observation, and the metric is named for what it is.
Evidence, not assertion
Answer it from stored state, in seconds, without opening a transport's logs and hoping they are still in retention. Every state transition writes an append-only, hashed evidence row carrying two separate times: when the world produced the signal, and when we recorded it.
73 event families are declared, each emitting evidence before anything reacts to it.
Do not start from a blank screen
A pack installs notification types, schemas, contracts, templates, routing defaults, preference categories and analytics definitions together — so a team starts with a shared vocabulary instead of assembling one.
The order has left. The tracking link is the entire point of the message.
A charge did not succeed and the recipient's service is at risk.
A short-lived passcode proving control of a channel. The single most latency-sensitive message the platform carries.
Tells a recipient their account was accessed from somewhere new, and gives them a way to act.
account.member.invited
account.member.removed
account.profile.updated
billing.invoice.created
billing.invoice.paid
billing.payment.failed
billing.subscription.cancelled
billing.subscription.renewed
commerce.order.cancelled
commerce.order.confirmed
commerce.order.delivered
commerce.order.shipped
commerce.refund.issued
32 packs: 16 sector, 13 functional and 3 compliance.
AI where it helps, guardrails where they matter
Drafting is a stage inside the template rail, never a bypass around it. Generated content enters as a draft and traverses variable validation, preview, review and a recorded human approval like anything else.
A draft referencing a variable the schema does not declare fails mechanical validation.
Publication is refused when the content changed after approval. An approval of some version is not an approval of this one.
A drafted template is marked as drafted for its whole life. Approval does not launder where it came from.
Respect preferences automatically
Preferences, consent, suppressions, quiet hours and rate limits are evaluated as part of the communication lifecycle. You call the same endpoint; the decision is made once, recorded, and explainable afterwards.
They do not drop. Unless the contract's time to live expires first — in which case the notification is expired, with evidence.
Not an error and never a silent drop. Every suppression carries a registered reason and a decision.
No contract, purpose or category overrides it.
54 registered reason codes, so “why was this suppressed?” is answerable in a support conversation without reading code.
Process first, store less
One rail runs over every payload class — notifications, inbound mail, attachments, ticket messages, webhook bodies. A field your contract does not declare is dropped before persistence, so it never needs classifying and never needs protecting.
Detection is deterministic and authoritative. A card number is caught by a checksum, not by resemblance — an order reference that looks like a card is not one, and an embedding never gets a say in a security decision.
Measured, not claimed
Consent, suppression, quiet hours, jurisdiction, sector and every installed compliance pack — 27 policies can bear on a single send, and the ones that apply to a given workspace are evaluated to one decision. Not a claim somebody typed: a benchmark runs the real decision plane on every build and fails over the budget, so this page cannot state a number the product no longer meets.
The policy plane takes a context and returns a decision. No database, no network, no ports — pure computation over rules held in memory. That is the reason for the number, and the reason it has to stay that way.
One database read inside one rule. The day a rule needs to look something up the budget is gone, and no ordinary test would notice — so the benchmark runs on every build and fails over 1ms.
The whole request is slower than the decision inside it: reading a contract, a recipient and their preferences takes real reads against real storage. We publish the decision number because it is the one we control absolutely.
Measured over 50,000 runs against the heaviest configuration — every compliance pack installed — rather than the lightest. The full request is slower than the decision inside it, and we publish the decision because it is the part we control absolutely.
Drop-in customer experiences
Preferences, consent and data requests your users control, embedded in your product and enforced by the same rail that delivers. Each renders in a shadow root, so your CSS cannot break it and its styles cannot leak into your page.
The recipient's own view of what they receive and how. The single most-requested surface, and the one that turns an opt-out into a downgrade.
The workspace API key never reaches the browser. Widgets authenticate with a short-lived, recipient-scoped token minted server-side — and the widget refuses a token that looks like an API key.
API first
Behind the call: contract resolution, schema validation, classification, policy evaluation, preference and consent resolution, template selection, routing, dispatch, evidence capture and webhook fan-out. None of it appears in the signature.
Every failure names a registered reason code and its decision. A recipient who opted out is their choice, not your bug, and the SDK tells you which.
A policy denial is never retried — it would produce the identical denial forever and bury the outcome.
Constant-time verification with a timestamp tolerance, so a captured payload cannot be replayed indefinitely.
106 endpoints and 134 operations, generated from the same document the SDK is built from.
Compliance packs
On the roadmap · phase 2
Compliance packs are sets of executable controls — classification, detection, minimisation, redaction, retention and subject-access workflows — that run in the same pipeline as your sends. The control happens before sensitive data reaches logs, search or analytics, not after.
PII classification, purpose limitation, consent records, preference evidence, retention, DSAR export and deletion, field minimisation, redacted analytics and search, audit evidence.
PHI vocabulary and schemas, minimum-necessary controls, strict search and log redaction, purpose-of-use, restricted access, break-glass with audit, retention and evidence.
PAN detection with Luhn validation, CVV and context detection, a forbidden-field dictionary, block and quarantine, redaction, template and payload inspection, violation evidence.
A compliance pack is a set of technical controls. It is not a certification, an attestation or a legal status, and we will not describe it as one. Genuine standing under health-privacy law also needs agreements no software can sign for you.
What is built, and what is next
A page cannot let you tell shipped from planned unless it says so. These are the rails, with their real status from the registry that drives the build.
| Rail | Tier | Status |
|---|---|---|
| Analytics Rail | Core | Available |
| Delivery Rail | Core | Available |
| Notification Rail | Core | Available |
| Preference Rail | Core | Available |
| Preprocessing Rail | Core | Available |
| Template Rail | Core | Available |
| Inbound Email Rail | Add-on | Phase 1.5 |
| Search Rail | Add-on | Phase 1.5 |
| Team Inbox Rail | Add-on | Phase 1.5 |
| Compliance Rail | Add-on | Phase 2 |
| Document Rail | Add-on | Phase 2 |
| Invoice Intake Rail | Add-on | Phase 2 |
| Push Rail | Add-on | Phase 2 |
| Advanced Routing Rail | Add-on | Phase 2 |
| SMS Rail | Add-on | Phase 2 |
| Telegram Rail | Add-on | Phase 3 |
| Wallet Rail | Add-on | Phase 3 |
| Workflow / Journey Rail | Add-on | Phase 3 |
Built to compound
Templates share schemas. Packs share vocabulary. Widgets read the same preferences the rail enforces. Evidence, analytics and search all read the same canonical objects. Each capability is more useful because the others exist — which is a harder thing to reproduce than a send endpoint.
58 notification types with declared payloads, channels and policies.
The widget writes what the delivery rail reads. There is no second copy to drift.
73 event families feeding the timeline, analytics and exports from the same rows.
54 reason codes shared by every decision, so an explanation means the same thing everywhere.
One layer, several jobs
One API, typed payloads, an SDK, signed webhooks and fewer delivery edge cases to discover in production.
Reusable communication patterns, an embeddable preferences centre and engagement measured honestly.
A timeline that answers “did they get it?” without a provider's logs.
Classification, minimisation, retention and evidence configured once and applied on every send.
Designed for teams building
Each sector pack installs the notification types, contracts and templates that sector needs, drawn from the same dictionary.
We do not show customer logos. When there are references worth naming, they will appear here with permission and not before.
Pricing
Volume is a capacity dimension inside a plan, never the whole of it. Billing is metered from the evidence plane: a notification that policy denied produced no delivery and is not billed as one.
A rail your plan does not include is absent rather than locked — it does not appear in your dashboard or your API reference.
Secure by design
Centralising communication means your application surfaces stop each holding delivery credentials, template implementations and logging pipelines of their own.
A key is bound to one workspace and one environment. A development key cannot cause a production delivery, and that is enforced before routing.
Recipient-scoped and minted server-side. The workspace key never reaches a browser.
Verify before you act, with replay protection.
Undeclared fields are dropped before persistence. A field never stored never needs protecting.
Every administrative mutation writes a before-hash and an after-hash.
Workspace and environment boundaries live at the storage key and the policy plane, not in a filter a handler could forget.
Start with one notification. Add templates, preferences, widgets and evidence as you need them.