The Temway Public API provides programmatic access to your workspace's emails, layouts, and branding.
It supports both read (retrieve published content) and write operations
(create, update, delete, duplicate), plus asynchronous rendering, publishing,
and test sending. It is the same surface the Temway MCP server drives, and is designed for
integrations with CMS platforms, automation tools, and custom applications.
Authentication is via API keys, which by default are scoped to a specific workspace and
carry resource scopes that gate each endpoint. An owner or admin can instead create a key scoped to every
workspace on the team — see List Workspaces. Keys are created and managed
from the Developer entry in the Temway app's main sidebar.
Base URL
https://api.temway.com/api/v1/public
All Public API endpoints are prefixed with /api/v1/public. The paths below omit this prefix for brevity.
Authentication
Every request must include an API key in the Authorization header using the Bearer scheme:
Are prefixed with tmw_live_ followed by 32 hex characters
Are scoped to a single workspace by default — a workspace-scoped key cannot access resources in other workspaces. An owner or admin can instead create a team-wide key, valid for every workspace on the team (see List Workspaces)
Carry resource scopes that gate each endpoint (new keys default to all scopes, narrowable at create)
Can have an optional expiration date
Are hashed (SHA-256) at rest — the full key value is only shown once at creation time
Scopes
Each endpoint requires a specific scope. Keys created from the dashboard default to every scope; you can narrow a key to least privilege.
Scope
Grants
emails:read
Read published + draft emails, render jobs, and send jobs
emails:write
Create, update, delete, duplicate emails; export, publish, and test-send
layouts:read
Read published + draft layouts
layouts:write
Create, update, delete, duplicate, and publish layouts
espSync:read
List ESP connections; poll ESP sync job status
espSync:write
Push a rendered email to a connected ESP as an editable template
GET /branding requires emails:readorlayouts:read.
GET /me requires any valid key (no scope gate — it resolves the key itself). Authoring an
email (emails:write) and pushing it to an ESP (espSync:write) are deliberately
separate scopes — a key that can write emails cannot push to a third-party ESP unless granted
espSync:write too.
Rate Limiting
The Public API applies per-key rate limits with in-memory sliding windows. Limits vary by endpoint group:
Endpoint Group
Limit
Window
Read (all GET)
60 requests
60 seconds (1 req/sec average)
Write (POST/PATCH/DELETE for emails & layouts)
30 requests
60 seconds (1 req/2 sec average)
Render (export & test-send)
20 requests
60 seconds (1 req/3 sec average)
Rate limit headers are included in every response:
The Retry-After header (seconds) is only present when the per-minute limit has been exceeded and
the response status is 429. Test sends are additionally subject to a per-user rolling 24-hour
cap derived from your plan (see Test Send Email) — that quota returns a
separate error body.
Errors
The Public API returns consistent error responses with the following structure:
API key expired, missing the required scope, or not authorized for this workspace
403
PLAN_LIMIT
A plan limit or feature gate was exceeded (e.g. email count, monthly exports). Body includes suggestedPlan.
404
NOT_FOUND
Requested resource does not exist or is not in the expected state
409
CONFLICT
State conflict — e.g. a render job is not completed, the wrong render target was supplied, or no email-target HTML is available (see Get Published Email HTML)
409
RENDER_NOT_READY
The referenced render job isn't a completed target='email' render for this email yet (see Push Email to ESP)
422
VALIDATION_ERROR
Malformed request body or invalid email/layout content (rejected by the schema validator)
429
TOO_MANY_REQUESTS
Per-minute rate limit exceeded. Check the Retry-After header.
429
RATE_LIMIT_EXCEEDED
Test-send daily quota exceeded. Body carries limit, used, windowHours, plan, and optional suggestedPlan.
Pagination
List endpoints return paginated results with the following shape:
Use the page and limit query parameters to navigate. Default limit is 20, maximum is 100.
Async Jobs (Export, Publish, Send)
Three email operations are asynchronous and follow a deliberate two-step pattern — you trigger
a job, then poll it to completion by its explicit ID:
Export (POST /:id/export) enqueues a render job and returns { jobId, status } with HTTP 202. Each export renders exactly one target — email (MJML/table HTML) or web (semantic HTML5) — set by the target query param.
Publish (POST /:id/publish) consumes a completedtarget='web' render job (pass its renderJobId): copies the web HTML into the email, sets status='published', and snapshots a version.
Test send (POST /:id/test-send) consumes a completedtarget='email' render job (pass its renderJobId) and enqueues a send to the to address.
Poll render jobs via GET /:id/render-jobs/:jobId (or /latest) and send jobs via
GET /:id/send-jobs/:jobId (or /latest). Always reference a render job by its
explicit ID — never assume "latest completed", since a newer web-only publish job can shadow an email
render.
Emails
All email endpoints are mounted under /workspaces/:workspaceId/emails.
List Published Emails
GET /workspaces/:workspaceId/emails API Key
Retrieve a paginated list of published emails in a workspace. Only emails with status <code>published</code> are returned. Supports optional search by name or subject.
Path Parameters
Name
Type
Description
workspaceId
string
Workspace ID
Query Parameters
Name
Type
Description
Required
page
integer
Page number (default: 1)
No
limit
integer
Items per page (default: 20, max: 100)
No
search
string
Search query — matches against email name or subject
No
Response
{
"data": [
{
"id": "email_abc123",
"name": "Welcome Email",
"subject": "Welcome to Temway!",
"preheaderText": "Get started with your account",
"tags": ["onboarding"],
"status": "published",
"updatedAt": "2026-06-26T12:00:00.000Z"
}
],
"total": 1,
"page": 1,
"limit": 20
}
Status Codes
Code
Description
200
List of published emails
401
Missing or invalid API key
403
API key lacks emails:read scope or is not authorized for this workspace
429
Rate limit exceeded
Get Published Email
GET /workspaces/:workspaceId/emails/:id API Key
Retrieve a single published email's metadata by its ID. Returns 404 if the email does not exist or is not in <code>published</code> status.
Path Parameters
Name
Type
Description
workspaceId
string
Workspace ID
id
string
Email ID
Response
{
"id": "email_abc123",
"name": "Welcome Email",
"subject": "Welcome to Temway!",
"preheaderText": "Get started with your account",
"tags": ["onboarding"],
"status": "published",
"updatedAt": "2026-06-26T12:00:00.000Z"
}
Status Codes
Code
Description
200
Published email metadata
401
Missing or invalid API key
403
API key lacks emails:read scope or is not authorized for this workspace
404
Email not found or not published
429
Rate limit exceeded
Get Published Email HTML
GET /workspaces/:workspaceId/emails/:id/html API Key
Retrieve the <strong>email-target</strong> HTML (<code>emails.html</code> — MJML-rendered, table-based) of a published email, along with its plain-text alternative. Returns 404 if the email is not found or not published. Returns 409 if no email-target HTML is available — this happens when the email has never been rendered as an email target, or after a <code>target='web'</code> publish (which populates web HTML but nulls the email-target HTML). Run <code>POST /:id/export?target=email</code> to populate it.
Path Parameters
Name
Type
Description
workspaceId
string
Workspace ID
id
string
Email ID
Response
{
"html": "<!DOCTYPE html>\n<html>...",
"text": "Plain text version of the email...",
"subject": "Welcome to Temway!",
"preheaderText": "Get started with your account",
"updatedAt": "2026-06-26T12:00:00.000Z"
}
Status Codes
Code
Description
200
Email-target HTML returned
401
Missing or invalid API key
403
API key lacks emails:read scope or is not authorized for this workspace
404
Email not found or not published
409
No email-target HTML available — export with ?target=email first
429
Rate limit exceeded
Get Draft Email (for editing)
GET /workspaces/:workspaceId/emails/:id/edit API Key
Retrieve the full email row in <strong>any</strong> status (including <code>draft</code>), including its <code>content</code> object. This is the endpoint authoring clients use to fetch what they are editing. Distinct path keeps the published-only contract of <code>GET /:id</code> untouched.
Path Parameters
Name
Type
Description
workspaceId
string
Workspace ID
id
string
Email ID
Status Codes
Code
Description
200
Full email row (any status, includes content)
401
Missing or invalid API key
403
API key lacks emails:read scope or is not authorized for this workspace
404
Email not found in this workspace
429
Rate limit exceeded
Create Email
POST /workspaces/:workspaceId/emails API Key
Create a new draft email in the workspace. <code>content</code> is the email-builder block tree (validated leniently; malformed content is normalized and logged, never rejected with a hard error). Subject to the plan's email-count limit (returns 403 <code>PLAN_LIMIT</code> when exceeded).
Missing emails:write scope, not authorized, or PLAN_LIMIT (email count reached)
422
Malformed request body
429
Rate limit exceeded
Update Email
PATCH /workspaces/:workspaceId/emails/:id API Key
Update an existing email. All fields are optional; pass only what you want to change. Updating <code>content</code> does not automatically re-publish — call the publish flow to refresh the stored HTML.
DELETE /workspaces/:workspaceId/emails/:id API Key
Soft-delete an email. The row is retained (it no longer counts toward plan limits) and is excluded from list results. Returns an empty body.
Path Parameters
Name
Type
Description
workspaceId
string
Workspace ID
id
string
Email ID
Status Codes
Code
Description
204
Email deleted (empty body)
401
Missing or invalid API key
403
Missing emails:write scope or not authorized
404
Email not found in this workspace
429
Rate limit exceeded
Duplicate Email
POST /workspaces/:workspaceId/emails/:id/duplicate API Key
Create a draft copy of an email. The copy resets to <code>status='draft'</code> with no rendered HTML. Subject to the plan's email-count limit.
Path Parameters
Name
Type
Description
workspaceId
string
Workspace ID
id
string
Email ID to duplicate
Request Body
{
"name": "Copy of Welcome Email" // optional — defaults to "Copy of <original>"
}
Status Codes
Code
Description
201
Duplicated email created (status: draft)
401
Missing or invalid API key
403
Missing emails:write scope, not authorized, or PLAN_LIMIT
404
Source email not found in this workspace
422
Malformed request body
429
Rate limit exceeded
Create Email from Layout
POST /workspaces/:workspaceId/emails/from-layout API Key
Create a new draft email seeded from a published layout's content. The email's <code>layoutId</code> is linked to the source layout. Subject to the plan's email-count limit.
Missing emails:write scope, not authorized, or PLAN_LIMIT
404
Layout not found in this workspace
422
Malformed request body
429
Rate limit exceeded
Export (Render) Email
POST /workspaces/:workspaceId/emails/:id/export API Key
Enqueue an asynchronous render job. Each job renders exactly <strong>one</strong> target: <code>email</code> (MJML/table HTML, for inboxes) or <code>web</code> (semantic HTML5, for share/template embeds). Returns the job ID with HTTP 202 — poll <code>GET /:id/render-jobs/:jobId</code> until <code>status='completed'</code>, then read <code>outputHtml</code> (email target) or <code>outputHtmlWeb</code> (web target). Subject to the plan's monthly export limit.
Missing emails:write scope, not authorized, or PLAN_LIMIT (monthly exports)
404
Email not found in this workspace
429
Rate limit exceeded
Get Render Job
GET /workspaces/:workspaceId/emails/:id/render-jobs/:jobId API Key
Poll a render job by its explicit ID. <code>outputHtml</code> is populated only for email-target jobs; <code>outputHtmlWeb</code> only for web-target jobs. Use <code>/render-jobs/latest</code> for the most recent job (registered before the parametric route).
Path Parameters
Name
Type
Description
workspaceId
string
Workspace ID
id
string
Email ID
jobId
string
Render job ID (or 'latest')
Response
{
"id": "rjob_xyz789",
"status": "completed", // pending | processing | completed | failed
"target": "email",
"outputHtml": "<!DOCTYPE html>...", // email target only (null for web)
"outputHtmlWeb": null, // web target only (null for email)
"errorMessage": null,
"createdAt": "2026-07-02T10:00:00.000Z",
"completedAt": "2026-07-02T10:00:08.000Z"
}
Status Codes
Code
Description
200
Render job details
401
Missing or invalid API key
403
Missing emails:read scope or not authorized
404
Email or render job not found
429
Rate limit exceeded
Publish Email
POST /workspaces/:workspaceId/emails/:id/publish API Key
Publish an email by consuming a <strong>completed web-target render job</strong>. Copies the web HTML into the email, sets <code>status='published'</code>, and snapshots a version (enforcing the plan's version cap). The render job must belong to this email and have <code>outputHtmlWeb</code> (a 409 is returned for an email-target render or an incomplete job).
Path Parameters
Name
Type
Description
workspaceId
string
Workspace ID
id
string
Email ID
Request Body
{
"renderJobId": "rjob_xyz789" // required — must be a completed target='web' render
}
Status Codes
Code
Description
200
Email published (status: published)
401
Missing or invalid API key
403
Missing emails:write scope or not authorized
404
Email or render job not found
409
Render job not completed, or has no web output (use target=web)
422
Malformed request body
429
Rate limit exceeded
Test Send Email
POST /workspaces/:workspaceId/emails/:id/test-send API Key
Send a test email to <code>to</code> by consuming a <strong>completed email-target render job</strong>. The render job must have <code>outputHtml</code> (a 409 is returned for a web-target render). Subject to a <strong>per-user rolling 24-hour cap</strong> derived from the plan (Free 5, Starter 20, Pro 50, Max 100). When the daily cap is hit, the response is 429 with <code>code='RATE_LIMIT_EXCEEDED'</code> and a body carrying <code>limit</code>, <code>used</code>, <code>windowHours</code>, <code>plan</code>, and optional <code>suggestedPlan</code>.
Path Parameters
Name
Type
Description
workspaceId
string
Workspace ID
id
string
Email ID
Request Body
{
"to": "[email protected]", // required — recipient email
"renderJobId": "rjob_xyz789" // required — must be a completed target='email' render
}
Response
{
"jobId": "sjob_qrs456",
"status": "pending"
}
Status Codes
Code
Description
202
Send job enqueued
401
Missing or invalid API key
403
Missing emails:write scope or not authorized
404
Email or render job not found
409
Render job not completed, or has no email output (use target=email)
422
Malformed request body
429
Per-minute rate limit OR daily test-send quota exceeded
Get Send Job
GET /workspaces/:workspaceId/emails/:id/send-jobs/:jobId API Key
Poll a send job by its explicit ID. <code>providerMessageId</code> is set on a successful delivery (for later webhook reconciliation). Use <code>/send-jobs/latest</code> for the most recent job.
All layout endpoints are mounted under /workspaces/:workspaceId/layouts. Layouts have no async render/export/send flow — publishing is synchronous.
List Published Layouts
GET /workspaces/:workspaceId/layouts API Key
Retrieve a paginated list of published layouts in a workspace. Only layouts with status <code>published</code> are returned. Supports optional search and category filters.
Path Parameters
Name
Type
Description
workspaceId
string
Workspace ID
Query Parameters
Name
Type
Description
Required
page
integer
Page number (default: 1)
No
limit
integer
Items per page (default: 20, max: 100)
No
search
string
Search query — matches against layout name or description
API key lacks layouts:read scope or is not authorized for this workspace
404
Layout not found or not published
429
Rate limit exceeded
Get Draft Layout (for editing)
GET /workspaces/:workspaceId/layouts/:id/edit API Key
Retrieve the full layout row in <strong>any</strong> status (including <code>draft</code>), including its <code>content</code> object. This is the endpoint authoring clients use to fetch what they are editing.
Path Parameters
Name
Type
Description
workspaceId
string
Workspace ID
id
string
Layout ID
Status Codes
Code
Description
200
Full layout row (any status, includes content)
401
Missing or invalid API key
403
API key lacks layouts:read scope or is not authorized for this workspace
404
Layout not found in this workspace
429
Rate limit exceeded
Create Layout
POST /workspaces/:workspaceId/layouts API Key
Create a new draft layout. <code>content</code> is the layout-editor descriptor. <code>canvas</code> constrains dimensions (width 200–1200, height 200–3000). Subject to the plan's layout-count limit.
Missing layouts:write scope, not authorized, or PLAN_LIMIT (layout count)
422
Malformed request body
429
Rate limit exceeded
Update Layout
PATCH /workspaces/:workspaceId/layouts/:id API Key
Update an existing layout. All fields are optional; pass only what you want to change. Updating <code>content</code> does not automatically re-publish.
DELETE /workspaces/:workspaceId/layouts/:id API Key
Soft-delete a layout. Returns an empty body.
Path Parameters
Name
Type
Description
workspaceId
string
Workspace ID
id
string
Layout ID
Status Codes
Code
Description
204
Layout deleted (empty body)
401
Missing or invalid API key
403
Missing layouts:write scope or not authorized
404
Layout not found in this workspace
429
Rate limit exceeded
Duplicate Layout
POST /workspaces/:workspaceId/layouts/:id/duplicate API Key
Create a draft copy of a layout. Subject to the plan's layout-count limit.
Path Parameters
Name
Type
Description
workspaceId
string
Workspace ID
id
string
Layout ID to duplicate
Request Body
{
"name": "Copy of Newsletter Template" // optional — defaults to "Copy of <original>"
}
Status Codes
Code
Description
201
Duplicated layout created (status: draft)
401
Missing or invalid API key
403
Missing layouts:write scope, not authorized, or PLAN_LIMIT
404
Source layout not found in this workspace
422
Malformed request body
429
Rate limit exceeded
Publish Layout
POST /workspaces/:workspaceId/layouts/:id/publish API Key
Publish a layout synchronously — sets <code>status='published'</code> against the layout's current content. Unlike emails, layouts have no async render step (they are SVG art, not MJML), so publishing takes no request body and no render job.
Path Parameters
Name
Type
Description
workspaceId
string
Workspace ID
id
string
Layout ID
Status Codes
Code
Description
200
Layout published (status: published)
401
Missing or invalid API key
403
Missing layouts:write scope or not authorized
404
Layout not found in this workspace
429
Rate limit exceeded
Branding
Get Workspace Branding
GET /workspaces/:workspaceId/branding API Key
Retrieve the workspace's branding configuration (colors, fonts, logo, sender defaults). Used to render emails with brand-consistent metadata. Requires <code>emails:read</code> <em>or</em> <code>layouts:read</code>.
Missing read scope or not authorized for this workspace
429
Rate limit exceeded
Integrations
Push a rendered email straight into a connected ESP as an editable template. Connecting an ESP account
(and re-verifying or removing one) is dashboard-only — the public API surface is read + push.
List ESP Connections
GET /workspaces/:workspaceId/integrations API Key
List the ESP accounts connected to this workspace. Never returns credential material — only display/status fields.
Missing espSync:read scope or not authorized for this workspace
429
Rate limit exceeded
Push Email to ESP
POST /workspaces/:workspaceId/emails/:id/integrations/:connectionId/push API Key
Enqueue a push of a <strong>completed email-target render job</strong> to a connected ESP as an editable template. The render job must belong to this email and have <code>outputHtml</code> (a 409 is returned for a web-target render or an incomplete job). The push itself is async — a background worker drains the queue; poll <a href='#get-esp-sync-status'>sync status</a> for the result.
Path Parameters
Name
Type
Description
workspaceId
string
Workspace ID
id
string
Email ID
connectionId
string
ESP connection ID (from List ESP Connections)
Request Body
{
"renderJobId": "rjob_xyz789", // required — must be a completed target='email' render
"templateName": "June Newsletter" // optional — defaults to the email's name
}
Missing espSync:write scope, not authorized, or PLAN_LIMIT (ESP integrations require a paid plan)
404
Email or ESP connection not found
409
Render job not completed, belongs to another email, or has no email-target output (code: RENDER_NOT_READY)
422
Malformed request body
429
Rate limit exceeded
Get ESP Sync Status
GET /workspaces/:workspaceId/emails/:id/integrations/sync-jobs/latest API Key
Poll the most recent ESP sync job for this email. Returns <code>null</code> if no push has been requested yet. <code>providerResourceId</code> / <code>providerResourceUrl</code> are set once the worker successfully creates the template on the ESP side.
Resolve the authenticated API key itself. Returns the team, scopes, and the workspace the key carries — <code>workspaceId</code> is <code>null</code> for a team-wide key. Not workspace-scoped (mounted without <code>:workspaceId</code>), so it has no scope gate — any valid key resolves itself. When <code>workspaceId</code> is <code>null</code>, the response also includes <code>defaultWorkspaceId</code> (the team's default workspace, or its first workspace if none is marked default) so a caller has somewhere to start. The remote MCP server uses this endpoint to derive the workspace from a bare key on session init, falling back to <code>defaultWorkspaceId</code> for a team-wide key.
Response
{
"workspaceId": "ws_abc", // null for a team-wide key
"defaultWorkspaceId": null, // only present when workspaceId is null
"teamId": "team_xyz",
"scopes": ["emails:read", "emails:write", "layouts:read", "layouts:write", "espSync:read", "espSync:write"]
}
Status Codes
Code
Description
200
Key identity
401
Missing or invalid API key
429
Rate limit exceeded
List Workspaces
GET /workspaces API Key
List the workspaces this key can reach. For a workspace-scoped key, returns an array of exactly one entry (itself). For a team-wide key, returns every workspace on the team. Not workspace-scoped (mounted without <code>:workspaceId</code>) and has no scope gate, matching <code>GET /me</code>. This is what a team-wide key uses to discover which workspace IDs it may pass as <code>:workspaceId</code> on every other endpoint.
API keys are created from the Developer entry in the Temway app's main sidebar. Once you have a key, you can
read published content, author new emails, render, publish, and test-send.