Maileditor Docs
PluginAPI referenceEmbedded API

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
QueryNotes
searchMatches the template name
per_pageDefault 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>"
}
FieldTypeRequiredNotes
namestringyesMax 255
templatestring or fileyesThe full document. html is accepted as an alias. Max 5 MB
typestringnofile 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
QueryNotes
typeprebuilt 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=prebuilt

200 is a bare object with no envelope:

{
  "source": "media",
  "html": "<!doctype html><html><body>…</body></html>"
}
FieldMeaning
htmlThe rendered markup. Empty string when nothing is stored
sourcemedia 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>"
}
FieldRequiredNotes
nameyesSent on every save, so a rename resends the markup
templateyesThe markup. html is accepted as an alias
edit_summarynoAccepted 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
QueryDefaultNotes
per_page8Capped 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}/html

200 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.

On this page