Templates
The end user's own templates inside the editor, including creating, saving as a draft, reading markup and version history.
All endpoints on this page require Authorization: Bearer {session_token} from the session endpoint and the embedded headers.
These are the templates a plugin's end users create and edit, scoped to the external_user_id on the session. Gallery templates are browsed through the catalog endpoints; the details and markup endpoints below can read either source with ?type=prebuilt.
One plugin cannot see another plugin's templates, and one end user cannot see another's. A session that declared no external_user_id is isolated too, and its work is unreachable once the session expires. If your users should keep their work across visits, send an external_user_id and sign it.
A template that is not the caller's returns 404, not 403. These templates never appear in the owner's dashboard.
List the user's templates
GET /public/plugin/v1/my-templates?search=welcome&per_page=15| Query | Notes |
|---|---|
search | Matches the template name |
per_page | Default 15 |
Newest-edited first. The list carries no html.
200
{
"message": "Templates retrieved successfully",
"result": {
"templates": {
"data": [
{
"id": 108,
"name": "Welcome Email",
"slug": "welcome-email",
"thumbnail": "https://…",
"external_user_id": "customer-user-4471",
"created_at": "2026-08-17T09:14:22.000000Z",
"updated_at": "2026-08-17T09:20:03.000000Z"
}
],
"links": { "first": "…", "last": "…", "prev": null, "next": null },
"meta": { "current_page": 1, "per_page": 15, "total": 1 }
}
}
}Create a template
POST /public/plugin/v1/my-templates{
"name": "Welcome Email",
"html": "<!doctype html><html><body>…</body></html>"
}| Field | Type | Required | Notes |
|---|---|---|---|
name | string | yes | Max 255 |
template | string or file | yes | The full document. html is accepted as an alias. Max 5 MB |
type | string | no | file to send template as an uploaded .html file |
The markup is sanitised before storage. A thumbnail is generated in the background, so thumbnail may be null at first. With custom storage attached, the file and thumbnail are written to that bucket.
201
{
"message": "Template created successfully",
"result": {
"template": { "id": 108, "name": "Welcome Email", "slug": "welcome-email", "thumbnail": null, "…": "…" }
}
}422 when name or template is missing, or the markup is over 5 MB.
402 quota_exceeded when saving would make this end user a new counted user and the plan's users allowance is spent. An end user who already has templates is never refused.
409 plugin_not_provisioned when the plugin's storage is still being set up. Retry shortly.
Template details
Metadata for a template, from either source.
GET /public/plugin/v1/my-templates/108
GET /public/plugin/v1/my-templates/41?type=prebuilt| Query | Notes |
|---|---|
type | prebuilt reads the gallery. Omit it for the end user's own |
The two id spaces are separate: gallery template 41 and own template 41 are different things. No markup is included.
200 for the end user's own:
{
"message": "Template retrieved successfully",
"result": {
"template": {
"id": 108,
"name": "Welcome Email",
"slug": "welcome-email",
"thumbnail": "https://…",
"type": "upload",
"created_by": null,
"external_user_id": "customer-user-4471",
"created_at": "2026-08-17T09:14:22.000000Z",
"updated_at": "2026-08-17T09:20:03.000000Z"
}
}
}200 for a gallery template:
{
"message": "Template retrieved successfully",
"result": {
"template": { "id": 41, "name": "Product Launch", "slug": "product-launch", "thumbnail": "https://…", "type": "prebuilt", "is_premium": false, "…": "…" }
}
}?type=prebuilt respects curation: a gallery template outside the plugin's selected categories returns 404. It also requires the template_gallery_access plan feature; without it the response is 402 feature_not_in_plan.
404 template_not_found for no such template, another end user's, another plugin's, or a gallery template this plugin does not expose.
Template HTML
GET /public/plugin/v1/my-templates/108/html
GET /public/plugin/v1/my-templates/41/html?type=prebuilt200 is a bare object with no envelope:
{
"source": "media",
"html": "<!doctype html><html><body>…</body></html>"
}| Field | Meaning |
|---|---|
html | The rendered markup. Empty string when nothing is stored |
source | media when read from the stored file. draft for roughly twenty seconds after a save, while the file is being written |
The markup is current either way. Same 402 and 404 rules as details.
Save a template
Saves a draft. The file, thumbnail and history entry are written in the background about twenty seconds later. Rapid saves are merged, so autosaving against this endpoint is safe.
PUT /public/plugin/v1/my-templates/108{
"name": "Welcome Email v2",
"template": "<!doctype html><html><body>…</body></html>"
}| Field | Required | Notes |
|---|---|---|
name | yes | Sent on every save, so a rename resends the markup |
template | yes | The markup. html is accepted as an alias |
edit_summary | no | Accepted and currently unused |
200
{
"message": "Draft updated successfully",
"result": {
"version": 3,
"status": "draft",
"last_edited_at": "2026-08-19T09:20:03.000000Z"
}
}version increments on every save. status is draft until the file is written, then uploaded. Right after a save the HTML endpoint returns source: "draft" with the new markup, and thumbnail keeps pointing at the previous render until the new one is ready.
With custom storage attached, the file, thumbnail and history copy go to that bucket and do not count against the plan's image_storage allowance.
404 under the same rule as details. 422 when name or template is missing.
Version history
Both history endpoints require the version_history plan feature; without it they return 402 feature_not_in_plan. Snapshots are only written while the plan includes the feature, so upgrading starts history from the moment of the upgrade.
A version holds the markup that a save replaced. A template saved only once has no history, and the newest entry is the version before your last save. The last 30 versions are kept per template.
List version history
GET /public/plugin/v1/my-templates/{template}/history| Query | Default | Notes |
|---|---|---|
per_page | 8 | Capped at 100 |
200
{
"message": "Template edit history retrieved successfully",
"result": {
"edit_histories": {
"data": [
{
"id": 412,
"template_id": 87,
"title": "Version 3 - 2026-09-01 11:04:22",
"type": null,
"edited_at": "2026-09-01T11:04:22.000000Z"
}
],
"links": { "first": "…", "last": "…", "prev": null, "next": null },
"meta": { "current_page": 1, "per_page": 8, "total": 3 }
}
}
}title is a label, not a parseable field. There is no markup here; fetch versions one at a time.
402 feature_not_in_plan, 404 under the same rule as details.
HTML of a past version
GET /public/plugin/v1/my-templates/{template}/history/{history}/html200 is bare, like the template HTML endpoint:
{
"source": "history",
"html": "<!doctype html><html>…</html>"
}To roll back, fetch the version here and PUT it to save a template. There is no restore endpoint.
402 feature_not_in_plan. 404 when the template is not the caller's or the version does not belong to that template.

.png)