Skip to main content

Overview

Templates define the content of your notifications. Each template belongs to a service and can include personalized fields that are filled in when you send a notification.

Template Types

Notify supports three notification channels:

Email

HTML and plain text emails with subject lines and optional file attachments

SMS

Text messages up to 918 characters (multiple SMS parts)

Letter

Printed and posted physical letters with optional Welsh translation

Template Structure

Core Fields

# From app/models.py - Template model
class Template:
    id: UUID                    # Unique identifier
    name: str                   # Template name (for reference)
    template_type: str          # "email", "sms", or "letter"
    content: str                # Message body
    subject: str                # Email/letter subject (required for email & letter)
    
    service_id: UUID            # Parent service
    created_by_id: UUID         # Creator
    version: int                # Version number (starts at 0)
    
    archived: bool              # Soft deleted?
    hidden: bool                # Hidden from UI?
{
  "name": "Password reset",
  "template_type": "email",
  "subject": "Reset your password",
  "content": "Hi ((name)),\n\nClick this link to reset: ((reset_url))",
  "has_unsubscribe_link": false
}

Personalization

Add dynamic content using double parentheses syntax:

Basic Personalization

Hi ((name)),

Your reference number is ((reference)).
When sending, provide values:
{
  "name": "Sarah",
  "reference": "ABC-123-XYZ"
}
Result:
Hi Sarah,

Your reference number is ABC-123-XYZ.

Placeholder Rules

  • Placeholders are case-sensitive: ((Name))((name))
  • Must contain only letters, numbers, underscores, and hyphens
  • Cannot start with a number
  • Maximum 255 characters

Conditional Content

Notify doesn’t support conditional logic in templates. Handle conditions in your application:
# Instead of conditionals in template, use different templates
if user.premium:
    template_id = PREMIUM_WELCOME_TEMPLATE
else:
    template_id = STANDARD_WELCOME_TEMPLATE

Template Versioning

Templates are versioned - every change creates a new version:
1

Initial Creation

Template created with version = 0
2

Updates

Each edit increments version: 0 → 1 → 2 → 3…
3

Notifications Reference Version

Each notification stores the template version used, ensuring content doesn’t change retroactively
# From app/models.py - TemplateHistory tracks all versions
class TemplateHistory:
    id: UUID              # Same as original template
    version: int          # Increments with each edit
    content: str          # Content at this version
    subject: str
    updated_at: datetime
    created_by_id: UUID   # Who made this version
You cannot delete a version once created. Notifications reference specific versions to maintain audit trail.

Email-Specific Features

Email templates can include automatic unsubscribe links:
template.has_unsubscribe_link = True
# Notify automatically adds an unsubscribe link to the email
From source code: has_unsubscribe_link can only be true for email templates.

Email Attachments

Email templates support file attachments:
# From app/models.py - TemplateEmailFile
class TemplateEmailFile:
    filename: str              # File name
    link_text: str            # Optional link text
    retention_period: int     # Weeks to retain (default: 1)
    validate_users_email: bool # Verify recipient email before download
    template_id: UUID
    template_version: int
Attachments are uploaded separately and linked to template versions.

Letter-Specific Features

Postage Options

# From app/constants.py
POSTAGE_TYPES = [
    "first",      # First class (1-2 days)
    "second",     # Second class (2-3 days)  
    "economy",    # Economy (3-5 days)
    "europe",     # European international
    "rest-of-world" # Worldwide international
]

Welsh Language Support

Letter templates support bilingual content:
# From app/constants.py
class LetterLanguageOptions:
    english = "english"
    welsh_then_english = "welsh_then_english"

# In template:
letter_languages: "welsh_then_english"
letter_welsh_subject: "Atgoffa Treth"  
letter_welsh_content: "Annwyl ((title)) ((surname)),\n\nMae..."
{
  "letter_languages": "english",
  "subject": "Tax Reminder",
  "content": "Dear ((name)),\n\nYour payment is due."
}

Letter Contact Blocks

Letters include a contact block (return address):
service_letter_contact_id: UUID  # References ServiceLetterContact

# Contact block appears on letter:
service.get_default_letter_contact()
# Returns formatted address block

Pre-compiled Letters

Special template for uploading PDFs directly:
# From app/constants.py
PRECOMPILED_TEMPLATE_NAME = "Pre-compiled PDF"

# This template is:
- hidden = True
- template_type = "letter"
- name = "Pre-compiled PDF"
Used for sending PDF files you’ve already formatted.

Template Folders

Organize templates in folders:
# From app/models.py
class TemplateFolder:
    id: UUID
    service_id: UUID
    name: str
    parent_id: UUID  # For nested folders
    
    # Folders can have permissions
    users_with_permission: List[UUID]  # User IDs who can access
Folders support:
  • Nested hierarchies (folders within folders)
  • Per-folder user permissions
  • Organizing by campaign, team, or purpose

Template Redaction

Hide sensitive personalization data from logs:
# From app/models.py
class TemplateRedacted:
    template_id: UUID
    redact_personalisation: bool  # Hide personalization in API responses
When redact_personalisation = true, API responses won’t show the personalization values sent with notifications.

API Response Format

{
  "id": "template-id-uuid",
  "type": "email",
  "created_at": "2026-03-03T10:30:00.000000Z",
  "updated_at": "2026-03-03T12:45:00.000000Z",
  "created_by": "[email protected]",
  "version": 3,
  "body": "Hi ((name)),\n\nYour code is ((code)).",
  "subject": "Your verification code",
  "name": "Email verification",
  "personalisation": {
    "name": {
      "required": true
    },
    "code": {
      "required": true
    }
  },
  "postage": null,
  "letter_contact_block": null
}

Best Practices

Use clear, descriptive names:
  • ✅ “Password reset - English”
  • ✅ “Appointment reminder - 48h”
  • ❌ “Template 1”
  • ❌ “test”
  • Always test with real data before going live
  • Provide fallback values for optional fields in your code
  • Don’t include sensitive data unless necessary
  • Use redaction for passwords, payment details, etc.
  • Keep SMS under 459 characters (3 message parts) when possible
  • Email subject lines under 78 characters display better
  • Test letter templates - they render as PDF
  • Include clear call-to-action
  • Don’t make breaking changes (removing placeholders)
  • Create new templates for major content changes
  • Document version changes in your application
  • Test new versions before switching

Build docs developers (and LLMs) love