Skip to Content
Configuration

Configuration

Config file path defaults to /config/config.yaml. Override with CONFIG_PATH. Use ${VAR} / ${VAR:-default} in config and in template.yaml. Unset required ${VAR} values fail startup (no empty secrets).

Special env names: CONFIG_PATH, TEMPLATES_DIR, PORT (default 3000), LOG_LEVEL, NODE_ENV. Everything else is whatever your YAML references.

You must define auth, and at least one of email.accounts or sms.accounts.

Auth: API key

auth: type: apiKey header: x-notifier-api-key # optional; this is the default value: ${NOTIFIER_API_KEY}

Send that header on /email/send and /sms/send. /health and /ready need no auth.

Auth: HMAC

auth: type: hmac header: x-notifier-signature # optional; this is the default secret: ${NOTIFIER_SIGNING_SECRET} tolerance: 300 # seconds

Header value: t=<unix-seconds>,v1=<hmac-sha512-hex>.

Message: t + "." + exact raw JSON body. Millisecond timestamps are rejected. Outside tolerance → 401.

import crypto from 'node:crypto'; export function sign(body, secret) { const t = Math.floor(Date.now() / 1000); const v1 = crypto .createHmac('sha512', secret) .update(`${t}.`) .update(body) .digest('hex'); return `t=${t},v1=${v1}`; }

Email accounts

email: accounts: zeptomail: type: zeptomail from: ${MAIL_FROM_EMAIL} apiKey: ${ZEPTOMAIL_API_KEY} # optional: fromName, bounceAddress ses: type: ses from: ${MAIL_FROM_EMAIL} region: ${AWS_REGION} accessKeyId: ${AWS_ACCESS_KEY_ID} secretAccessKey: ${AWS_SECRET_ACCESS_KEY} defaults: account: zeptomail renderer: react-email

Pick an account per request (account), per template (account in template.yaml), or via email.defaults.account. Field merge order for from/subject/etc.: account → template → request (request wins).

email.defaults.renderer is the fallback when a template omits renderer. If email accounts exist and any template fails to load, the process refuses to start.

SMS accounts

sms: accounts: termii: type: termii apiKey: ${TERMII_API_KEY} from: ${TERMII_FROM} version: v3 # v3 | v4 channel: dnd # dnd | generic messageType: plain # plain | unicode # optional: baseUrl (fixed host; then sendOptions.version is rejected) defaults: account: termii

SMS has no templates. Body text comes on the request.

Request limits

requestValidation: maxBodySize: 1048576 maxAttachmentSize: 10485760 maxAttachments: 10 allowedAttachmentMimeTypes: - application/pdf - image/png - image/jpeg

Omit the block to use defaults (1 MiB body, 10 MiB per attachment, 10 attachments, common MIME allowlist).

Private network

  • Place notifier behind internal load balancing / mesh. Do not expose send routes publicly.
  • Terminate TLS or mTLS at the edge.
  • Prefer HMAC when callers are other services (integrity + replay window).
  • /health and /ready are open for probes; they are not auth for sends.
Last updated on