Email-templates API
Auto-generated from the OpenAPI spec. Run
node docs-site/scripts/fetch-openapi.jsto regenerate.
About the blocks field (Visual Email Builder)
POST/PUT bodies on /api/v1/email/templates accept an optional blocks field, and
GET/POST/PUT responses echo it back:
nullor omitted — the template is in raw HTML mode.html_bodyis the template as written; there is no canvas structure behind it.- A JSON array of block objects — the template is in canvas mode. Each element is
{ id: string, type: "text" | "image" | "button" | "divider" | "spacer" | "html", props: {...} }, wherepropsis a block-type-specific object (for example, atextblock'spropshascontent,align,fontSize,color,bold,italic). The server does not validateprops's shape — that's the frontend's responsibility (frontend/src/renderer/src/lib/emailBlocks.ts) — it only requires thatblocksbe a JSON-serializable list of objects.
html_body is always required and is always the fully-rendered HTML, regardless of mode —
when saving a canvas-mode template, the frontend computes html_body from blocks client-side
before sending both together, so a client integrating directly with this API in canvas mode must
do the same (render blocks to HTML itself) rather than relying on the server to derive one from
the other.
GET /api/v1/email/templates
List Templates
Responses
- 200 — Successful Response
POST /api/v1/email/templates
Create Template
Request body
| Field | Required | Type | Description |
|---|---|---|---|
name | Yes | string | |
subject | Yes | string | |
html_body | Yes | string | |
blocks | No | array |
Responses
- 201 — Successful Response
- 422 — Validation Error
GET /api/v1/email/templates/{template_id}
Get Template
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
template_id | path | Yes | integer |
Responses
- 200 — Successful Response
- 422 — Validation Error
PUT /api/v1/email/templates/{template_id}
Update Template
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
template_id | path | Yes | integer |
Request body
| Field | Required | Type | Description |
|---|---|---|---|
name | No | string | |
subject | No | string | |
html_body | No | string | |
blocks | No | array |
Responses
- 200 — Successful Response
- 422 — Validation Error
DELETE /api/v1/email/templates/{template_id}
Delete Template
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
template_id | path | Yes | integer |
Responses
- 200 — Successful Response
- 422 — Validation Error
POST /api/v1/email-templates/images
Upload Email Template Image
Upload one image for a Visual Email Builder Image block. Manager-only.
Returns an absolute URL (baked in at upload time, not left relative, since the URL must resolve correctly inside an email opened outside the app) built from StudioSettings.tunnel_url — same pattern as app/routers/clients.py's invite_url construction. Falls back to a relative path when tunnel_url isn't set yet (e.g. local dev before named-tunnel provisioning), consistent with clients.py's own fallback behavior of still returning a usable (if not yet externally-resolvable) URL rather than blocking the upload.
Response body (201)
| Field | Type | Description |
|---|---|---|
url | string | Absolute URL for the uploaded image ({tunnel_url}/api/v1/email-templates/images/{filename}), ready to use as an Image block's source. Relative (no scheme/host) if the studio has not yet set up a tunnel URL. |
Responses
- 201 — Successful Response
- 422 — Validation Error
GET /api/v1/email-templates/images/{filename}
Get Email Template Image
Serve a previously-uploaded email image. Public — intentionally no auth dependency (see module docstring / §6.20b).
The filename path param is never trusted directly: it is sanitized (basename + allow-list + commonpath containment check, same technique as resolve_safe_photo_path) AND must start with the "email_" prefix this upload endpoint always gives its files (defense in depth, same technique as app/routers/widget.py::get_widget_product_photo) — any mismatch or missing file is a generic 404, never a 500.
Response body (200): the raw image file (binary), not JSON — served with the
content type of the uploaded file. No Authorization header required or checked;
this is the one deliberately public, no-auth endpoint on this page (see the
module docstring above) so the image loads inside an email opened in an external
mail client. A filename that doesn't start with the email_ prefix, or that
doesn't resolve to an existing file, returns a generic 404 — never a 500.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
filename | path | Yes | string |
Responses
- 200 — Successful Response
- 404 — Image not found (bad prefix, path traversal attempt, or file missing)
- 422 — Validation Error