Restore a previous version of a template
Reinstates a previous definition. A restore is an ordinary edit whose content happens to be old: it snapshots the state it replaces in the same transaction, so nothing is lost, the restore is itself in the history, and restoring the version it created undoes it. That is why this operation is NOT classified destructive, and why a test-environment credential may call it.
It bypasses the optimistic-concurrency check by design — "whatever is there now, make it this" is not a statement about what the caller last read — and it deletes no history.
The restored content is deployed to the organization's SES account like
any other save, so deploy_state on the returned template reads
pending until it lands.
Path parameters
slug string required The template's name (its slug), matched case-insensitively, or its UUID.
version integer required The version number, as returned by the versions collection. Version numbers are the handle on every kind here, including the kinds whose dashboard routes address a version by row id.
Responses
Errors follow the RFC 7807 problem format — see the error reference.
kind string required restored integer required The version number that was reinstated.
current object required The definition as it now stands, in the shape the kind's own read route returns.
version object required One row of a definition's history.
Fields of version:
version_number integer required created_at string<date-time> required When the snapshot was taken, which is when the edit that REPLACED this state was made — not when this state was authored.
actor object required Who made one edit. The label is resolved server-side — a person's name, an API key's name, an OAuth client's name — because the name lives in a different table per kind and a client can read none of them. It never carries a key, a token or a client secret; a revoked credential falls back to a short form of its id rather than rendering blank.
Fields of actor:
kind string enum required unknown is an honest value rather than an error: it is what rows predating actor recording carry, and what a write reached with no credential records.
One of: user, api_key, oauth_client, system, unknown
id string optional The users / api_keys / oauth_clients id, per kind. Absent for system and unknown.
label string required session string optional The HTTP request id of the edit — the same value on the matching audit row, so the two can be joined. It is NOT an MCP session id: the MCP transport is stateless and reads no session header.
content_hash string optional summary string required How this version differs from the one before it — +3 −1 lines for a source kind, renamed, type string→number for a schema kind. The oldest row says first recorded version.
current object optional Fields of current:
slug string required The template's name, used in the path (e.g. welcome).
name string required Same as slug — there is no separate display-name column.
subject string | null required The email's subject line, itself a Handlebars template. Null when the template carries none.
channel string | null required Always null. Templates carry no channel binding in the data model; the field is kept so existing readers keep working.
content_hash string | null required The optimistic-concurrency token. Send it back as expected_content_hash on a write to make that write conditional. It covers the BODY only, so a subject-only change does not move it.
deploy_state string enum required Whether this content is live in the org's SES account. pending means saved but not yet in SES — including on the response to a write of an already-deployed template, where SES is still serving the previous body. Poll GET /v1/templates/{slug} to see it settle.
One of: pending, deployed, failed
ses_template_name string | null required The name the template holds in SES ({namespace}--{name}); null before it has ever deployed.
deploy_error string | null required Why the last deploy failed. Null when nothing has.
deployed_at string<date-time> | null required When SES last accepted this template. Null while a deploy is outstanding, including on a write whose new content has not landed.
default_topic string | null required The topic NAME a send of this template opts recipients out of when the step names none. Resolved late, at send time, so it may name a topic that does not exist yet — requires[] on a write says whether it does.
default_consent_class string enum required "" — marketing, the default. transactional — sanctioned lifecycle mail, which is topic-exempt.
One of: ``, transactional
variables array<string> required The merge fields the body references, as stored on the row.
last_modified_at string<date-time> required requires array<object> optional What this template still needs — its default_topic, and every {{asset}} path it references — with the state of each. Present on writes only; absent on the reads, which resolve nothing.
Each entry in requires:
kind string enum required The namespace key is looked up in. Branch on this; never parse detail.
One of: template, segment, list, topic, identity, channel, attribute, activity_property, asset
key string required The identifier as the definition names it — a template name, a segment id or key, a from-address. Empty for a requirement about the ABSENCE of a reference: a send with no topic at all names no topic.
status string enum required ok — resolves and is usable now.
missing — nothing in this organization answers to that key. Correct
the reference, or create the thing.
not_ready — it exists, in a state that cannot be used yet. Usually
a wait, and retrying the identical request is the move. Treating this
as missing sends you hunting for a typo that is not there.
One of: ok, missing, not_ready
detail string required The state, in the words that say which state — deploy_state=pending, member_count=null (evaluation queued). For display and logs; do not parse it.
fix string required The call that clears this requirement, as an HTTP route where there is one and as a plain instruction where there is not (a template deploy is waited for, not called). Empty on an ok entry.
code is internal_error. The
request_id field can be quoted to SendOps support to investigate.
application/problem+json