Skip to content

SendGrid Template Versioning — Managing Template Changes and Rollbacks

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about SendGrid Template Versioning. We cover key concepts, practical examples, and best practices to help you master this topic.

SendGrid template versioning allows multiple versions of a template to exist simultaneously, with draft versions for testing and active versions for production, enabling safe template updates with rollback capability.

What You'll Learn

  • How template versions work in SendGrid
  • How to create, activate, and delete versions
  • How to implement a version management workflow

Why It Matters

Without versioning, updating a template immediately affects all users. If the update has a bug, every email sent is broken. Versioning lets you create a draft, test it with a subset of users, activate when ready, and roll back instantly if issues arise.

Real-World Use

DodaTech's marketing team uses template versions for A/B testing. Version 1 (active) serves 90% of users. Version 2 (draft) serves 10% with a new design. After confirming version 2 has higher click rates, they activate it globally. If engagement drops, they roll back to version 1.

Version Management

import requests

SENDGRID_API_KEY = "your-api-key"
BASE_URL = "https://api.sendgrid.com/v3"
HEADERS = {
    "Authorization": f"Bearer {SENDGRID_API_KEY}",
    "Content-Type": "application/json"
}

def list_template_versions(template_id):
    response = requests.get(
        f"{BASE_URL}/templates/{template_id}/versions",
        headers=HEADERS
    )
    versions = response.json()
    for v in versions:
        status = "ACTIVE" if v.get('active') == 1 else "DRAFT"
        print(f"Version ID: {v['id']} ({status}) - v{v.get('version')}")
    return versions

def create_version(template_id, subject, html_content, name="v2"):
    response = requests.post(
        f"{BASE_URL}/templates/{template_id}/versions",
        headers=HEADERS,
        json={
            "template_id": template_id,
            "subject": subject,
            "html_content": html_content,
            "name": name,
            "active": 0  # Draft
        }
    )
    version = response.json()
    print(f"Created version: {version['id']}")
    return version

def activate_version(template_id, version_id):
    response = requests.patch(
        f"{BASE_URL}/templates/{template_id}/versions/{version_id}",
        headers=HEADERS,
        json={"active": 1}
    )
    return response.status_code == 200

def get_active_version(template_id):
    versions = list_template_versions(template_id)
    for v in versions:
        if v.get('active') == 1:
            return v
    return None

Version Workflow

class TemplateVersionManager:
    def __init__(self, template_id):
        self.template_id = template_id
        self.api_url = f"{BASE_URL}/templates/{template_id}/versions"

    def promote_to_production(self, version_id):
        # Deactivate all other versions
        versions = requests.get(self.api_url, headers=HEADERS).json()
        for v in versions:
            if v['id'] != version_id and v.get('active') == 1:
                self.deactivate_version(v['id'])

        # Activate the target version
        return activate_version(self.template_id, version_id)

    def rollback(self):
        """Roll back to the previous active version"""
        versions = requests.get(self.api_url, headers=HEADERS).json()
        # Sort by version number, take the last active one
        active = [v for v in versions if v.get('active') == 1]
        if len(active) > 1:
            # There are multiple active versions
            sorted_versions = sorted(active, key=lambda x: x['version'], reverse=True)
            # Deactivate current, activate previous
            self.deactivate_version(sorted_versions[0]['id'])
            self.activate_version(sorted_versions[1]['id'])
            return True
        return False

    def deactivate_version(self, version_id):
        requests.patch(
            f"{self.api_url}/{version_id}",
            headers=HEADERS,
            json={"active": 0}
        )

Testing with Draft Versions

def send_test_with_version(template_id, version_id, test_email):
    """Send a test email using a specific template version"""
    from sendgrid import SendGridAPIClient
    from sendgrid.helpers.mail import Mail

    sg = SendGridAPIClient(SENDGRID_API_KEY)

    message = Mail(
        from_email='test@dodatech.com',
        to_emails=test_email
    )
    message.template_id = template_id
    message.dynamic_template_data = {
        'userName': 'Test User',
        'companyName': 'DodaTech'
    }

    # Activate the test version temporarily
    activate_version(template_id, version_id)

    # Send the test
    response = sg.send(message)

    # Restore the original active version
    # (implement rollback logic here)

    return response.status_code == 202

Common Mistakes

1. Editing the Active Version Directly

Never make changes to an active version. Always create a draft, test, and then promote to production.

2. Not Tracking Version Changes

Log which version was active when, what changed, and who promoted it. This audit trail helps debug issues.

3. Leaving Multiple Versions Active

Only one version should be active for production. Having multiple active versions causes inconsistent email rendering.

4. Forgetting to Test Draft Versions

Always send test emails from draft versions before promoting. Check rendering across email clients (Gmail, Outlook, Apple Mail).

5. Not Having a Rollback Plan

Before promoting a new version, ensure you can roll back to the previous one. Document the rollback procedure.

Practice Questions

  1. What is the difference between an active and a draft version?
  2. How do you create a new template version?
  3. How do you promote a draft to production?
  4. How do you roll back to a previous version?
  5. Why should multiple versions not be active simultaneously?

Answers

  1. Active versions serve production traffic; draft versions are for testing. 2. POST to /v3/templates/{id}/versions with active=0. 3. PATCH the version to set active=1 and deactivate others. 4. Deactivate the current active version and reactivate the previous one. 5. It causes inconsistent rendering across recipients.

Challenge

Build a template version management CLI that lists versions, creates drafts, promotes versions to production, rolls back to previous versions, and sends test emails with a specific version. Include version labeling for semantic versioning (1.0.0, 1.1.0, etc.).

FAQ

What is template versioning in SendGrid?

The ability to create multiple versions of the same template for testing and staged rollouts.

How many versions can a template have?

There is no hard limit, but best practice is to keep only the current active version and the latest draft.

Can I A/B test with template versions?

Yes. Use different versions for different user segments and track engagement metrics.

What happens if I activate a version with errors?

Emails will render with missing or broken content. Always test draft versions before activating.

Can I delete old template versions?

Yes. Use DELETE /v3/templates/{id}/versions/{version_id} to remove unused versions.

Mini Project

Build a template version management dashboard that: lists all templates and their versions, shows which version is active, creates new draft versions from the web UI, promotes drafts to production with one click, and supports rollback to any previous version with an audit log.

What's Next

  • Learn about attachments and inline images in SendGrid emails
  • Explore categories and unique arguments for email tracking
  • Continue to suppression groups for unsubscribe management

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro