SendOps manages email templates entirely through GitHub. There is no in-app template editor — you connect a GitHub repository, and SendOps syncs your template files, validates them, extracts variables, and deploys them to Amazon SES. Templates use **Handlebars** syntax for dynamic content.


  If you have existing email templates in your SES account, you can export them as Git-ready files and import them into SendOps. See [Importing from SES](/templates/importing-from-ses) for the step-by-step workflow.




## Connecting a GitHub repository

The GitHub connection is set up directly from the **Templates** page (not from Settings).


  <Step title="Open the Templates page">
    Navigate to **Templates** in the sidebar. If no repository is connected, you'll see the connection card.
  </Step>

  <Step title="Authenticate with GitHub">
    Click **Connect Repository**. You'll be redirected to GitHub to install the SendOps GitHub App on your account or organization. The app reads your repository contents and pull requests, and — so it can post validation checks and reviews back to you — it also has write access to pull request reviews and check runs.
  </Step>

  <Step title="Select a repository">
    Choose the **repository owner** and **repository name** from the dropdowns. Only repositories where the GitHub App is installed are shown.
  </Step>

  <Step title="Choose a branch">
    Select the branch SendOps should sync from (typically `main` or `master`). Only templates on this default branch are deployed to SES.
  </Step>

  <Step title="Set the subfolder (optional)">
    **Subfolder** is the primary path field — the folder in your repo that holds your SendOps files. SendOps reads your `sendops.json` manifest from here, and the manifest in turn names the explicit path to each template. Scoping to a subfolder lets several organizations share one repository. Leave it blank to use the repository root; the common convention is `sendops`.

    A second field, **Template Path**, lives under **Advanced**. It only matters for repositories *without* a `sendops.json` manifest — it names the directory templates are scanned from, and it is ignored whenever a manifest is present. Most repos use a manifest and can leave this untouched.
  </Step>


### Connection statuses

| Status | Meaning |
|--------|---------|
| **Active** | The GitHub App is installed and working. Pushes trigger automatic syncs. |
| **Disconnected** | The connection was removed from SendOps. Re-connect to resume syncing. |
| **Suspended** | The GitHub App installation was suspended on the GitHub side (e.g. by an org admin). Re-activate the app in GitHub to restore the connection. |

You can refresh the repository list at any time if you've added new repos to the GitHub App installation.

## Template sync

Once connected, SendOps syncs templates automatically whenever you push to the default branch. You can also trigger a manual sync by clicking **Sync Now**.

During a sync, SendOps:
1. Reads template files from the configured path
2. Validates each template (syntax and SES limits)
3. Extracts template variables
4. Deploys valid templates to SES (default branch only)

### Sync statuses

Each sync shows one of these statuses:

| Status | Meaning |
|--------|---------|
| **Synced** | All templates synced and deployed successfully. |
| **Synced with errors** | Some templates failed validation and were not deployed. |
| **Failed** | The sync could not complete (e.g. GitHub API error, permission issue). |

The sync indicator on the Templates page shows the last sync time and lets you view **sync history** — a list of past syncs with their status, timestamp, commit SHA, and any errors.

### Why a deploy might be skipped

A template is synced but not deployed to SES when:
- The template has validation errors
- The push was to a non-default branch
- The template file type is not supported

## Supported file types

SendOps recognizes these file extensions as templates:

- `.html`
- `.hbs`
- `.handlebars`

The file name (without extension) becomes the SES template name. For example, `welcome-email.hbs` creates an SES template named `welcome-email`.

## Manifest file

For advanced configuration, you can add a `sendops.json` manifest file to your repository root. The manifest lets you explicitly list which files to sync, map file names to specific SES template names, and define test data profiles that sync alongside your templates. Without a manifest, SendOps scans the template path for all supported files automatically.

See [Manifest File](/templates/manifest-file) for the full format reference.

## Handlebars syntax

Templates use [Handlebars](https://handlebarsjs.com/) syntax for dynamic content — not just simple `{{variable}}` substitution. SendOps supports the full set of built-in Handlebars block helpers:

### Variables

```handlebars
Hello, {{name}}!
Your order {{order_id}} has shipped to {{shipping_address}}.
```

### Conditional blocks

```handlebars
{{#if premium_member}}
  Thank you for being a premium member!
{{else}}
  Upgrade to premium for exclusive benefits.
{{/if}}

{{#unless unsubscribed}}
  You're receiving this because you subscribed.
{{/unless}}
```

### Iteration

```handlebars
{{#each items}}
  
    <td>{{this.name}}</td>
    <td>{{this.price}}</td>
  
{{/each}}
```

### Scoped context

```handlebars
{{#with shipping_address}}
  {{street}}, {{city}}, {{state}} {{zip}}
{{/with}}
```


  SendOps manages the template definition in SES, but your application provides the variable values when calling the SES `SendTemplatedEmail` or `SendBulkTemplatedEmail` API.


## Template listing

The Templates page shows all synced templates with two view modes:

- **Card view** — visual cards showing template name, validation status, and last sync time
- **Table view** — compact list with sortable columns

You can **search** by template name and **filter** by validation status (valid, invalid, all). A **branch selector** lets you switch between branches to view templates on any branch, though only the default branch deploys to SES.

## Template detail

Click any template to open its detail page, which has two tabs:

### Overview tab

The Overview tab shows:

- **Source and preview** — side-by-side view of the raw Handlebars source code on the left and the rendered HTML preview on the right
- **Validation errors** — if the template is invalid, errors are listed with the specific **line and column** number so you can locate issues quickly
- **Variables card** — a list of all variables extracted from the template, including their inferred types
- **SES deployment info** — the SES template name, region, and deployment status
- **Assets** — for git-backed templates, the [images the template references](/edge/asset-library), each with a thumbnail. Resolved images link to their detail; a reference that doesn't match a promoted image (a typo, or an image missing from your manifest) is flagged as **unresolved** so you can fix it

### Test Data tab

The Test Data tab lets you manage test data profiles for previewing and testing the template. See [Previews & Test Data](/templates/template-versioning) for full details.

## Template validation

Every synced template is automatically validated against two sets of rules:

**Handlebars syntax** — the template must be valid Handlebars. Unclosed blocks, malformed expressions, and syntax errors are caught and reported with line and column numbers.

**SES limits** — Amazon SES imposes limits on templates:

| Limit | Value |
|-------|-------|
| Maximum template file size | 500 KB |
| Maximum templates per region | 20,000 |

Templates that fail validation are still synced to SendOps (so you can see the errors) but are **not deployed to SES**.

## Variables extraction

SendOps automatically extracts all variables from your templates and displays them on the template detail page. The extractor understands Handlebars syntax, so it correctly identifies variables inside `{{#if}}`, `{{#each}}`, and `{{#with}}` blocks, and infers basic types (string, number, boolean, array, object) from how variables are used.

## What's next?

- [Pull Requests & Compare](/templates/github-integration) — review template changes through PRs and compare branches.
- [Previews & Test Data](/templates/template-versioning) — preview templates, manage test data profiles, and send test emails.
- [Manifest File](/templates/manifest-file) — control template naming and sync git-based test data profiles.
- [How Email Flows](/sending-email/email-flow) — understand how templates fit into the sending pipeline.