Public API Reference

Overview

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:

Authorization: Bearer tmw_live_a1b2c3d4e5f67890abcdef12345678

API keys:

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

ScopeGrants
emails:readRead published + draft emails, render jobs, and send jobs
emails:writeCreate, update, delete, duplicate emails; export, publish, and test-send
layouts:readRead published + draft layouts
layouts:writeCreate, update, delete, duplicate, and publish layouts
espSync:readList ESP connections; poll ESP sync job status
espSync:writePush a rendered email to a connected ESP as an editable template

GET /branding requires emails:read or layouts: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:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1748354400
Retry-After: 18

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:

{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable description"
  }
}
Status Error Code Description
401UNAUTHORIZEDMissing, invalid, or malformed API key
403FORBIDDENAPI key expired, missing the required scope, or not authorized for this workspace
403PLAN_LIMITA plan limit or feature gate was exceeded (e.g. email count, monthly exports). Body includes suggestedPlan.
404NOT_FOUNDRequested resource does not exist or is not in the expected state
409CONFLICTState 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)
409RENDER_NOT_READYThe referenced render job isn't a completed target='email' render for this email yet (see Push Email to ESP)
422VALIDATION_ERRORMalformed request body or invalid email/layout content (rejected by the schema validator)
429TOO_MANY_REQUESTSPer-minute rate limit exceeded. Check the Retry-After header.
429RATE_LIMIT_EXCEEDEDTest-send daily quota exceeded. Body carries limit, used, windowHours, plan, and optional suggestedPlan.

Pagination

List endpoints return paginated results with the following shape:

{
  "data": [
    { "id": "email_abc", "name": "Welcome Email", "...": "..." }
  ],
  "total": 42,
  "page": 1,
  "limit": 20
}

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:

  1. 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.
  2. Publish (POST /:id/publish) consumes a completed target='web' render job (pass its renderJobId): copies the web HTML into the email, sets status='published', and snapshots a version.
  3. Test send (POST /:id/test-send) consumes a completed target='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).

Path Parameters

Name Type Description
workspaceId string Workspace ID

Request Body

{
  "name": "Welcome Email",          // required, 1–255 chars
  "subject": "Welcome to Temway!",  // optional
  "preheaderText": "Get started",   // optional
  "layoutId": "layout_abc123",      // optional — inherit a layout's content
  "tags": ["onboarding"],           // optional
  "content": { /* email-builder block tree */ }  // required
}

Status Codes

Code Description
201 Email created (status: draft)
401 Missing or invalid API key
403 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.

Path Parameters

Name Type Description
workspaceId string Workspace ID
id string Email ID

Request Body

{
  "name": "New Name",              // optional
  "subject": "...",                // optional, nullable
  "preheaderText": "...",          // optional, nullable
  "tags": ["newsletter"],          // optional
  "content": { /* block tree */ }, // optional
  "thumbnailUrl": "https://..."    // optional, nullable
}

Status Codes

Code Description
200 Email updated
401 Missing or invalid API key
403 Missing emails:write scope or not authorized
404 Email not found in this workspace
422 Malformed request body
429 Rate limit exceeded

Delete Email

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.

Path Parameters

Name Type Description
workspaceId string Workspace ID

Request Body

{
  "layoutId": "layout_abc123",     // required
  "name": "Newsletter — June",     // required, 1–255 chars
  "subject": "Your June newsletter" // optional
}

Status Codes

Code Description
201 Email created from layout (status: draft)
401 Missing or invalid API key
403 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.

Path Parameters

Name Type Description
workspaceId string Workspace ID
id string Email ID

Query Parameters

Name Type Description Required
target string Render target: 'email' (default) or 'web' No

Response

{
  "jobId": "rjob_xyz789",
  "status": "pending",
  "createdAt": "2026-07-02T10:00:00.000Z"
}

Status Codes

Code Description
202 Render job enqueued
401 Missing or invalid API key
403 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.

Path Parameters

Name Type Description
workspaceId string Workspace ID
id string Email ID
jobId string Send job ID (or 'latest')

Response

{
  "id": "sjob_qrs456",
  "status": "completed",        // pending | processing | completed | failed
  "providerMessageId": "re_abc...",
  "errorMessage": null,
  "createdAt": "2026-07-02T10:01:00.000Z",
  "completedAt": "2026-07-02T10:01:03.000Z"
}

Status Codes

Code Description
200 Send job details
401 Missing or invalid API key
403 Missing emails:read scope or not authorized
404 Email or send job not found
429 Rate limit exceeded

Layouts

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 No
category string Filter by layout category No

Response

{
  "data": [
    {
      "id": "layout_abc123",
      "name": "Newsletter Template",
      "description": "Two-column newsletter layout",
      "category": "newsletter",
      "tags": ["newsletter", "two-column"],
      "thumbnailUrl": "https://cdn.temway.com/thumbnails/layout_abc123.png",
      "status": "published",
      "updatedAt": "2026-06-26T12:00:00.000Z"
    }
  ],
  "total": 1,
  "page": 1,
  "limit": 20
}

Status Codes

Code Description
200 List of published layouts
401 Missing or invalid API key
403 API key lacks layouts:read scope or is not authorized for this workspace
429 Rate limit exceeded

Get Published Layout

GET /workspaces/:workspaceId/layouts/:id API Key

Retrieve a single published layout's metadata by its ID. Returns 404 if the layout does not exist or is not in <code>published</code> status.

Path Parameters

Name Type Description
workspaceId string Workspace ID
id string Layout ID

Response

{
  "id": "layout_abc123",
  "name": "Newsletter Template",
  "description": "Two-column newsletter layout",
  "category": "newsletter",
  "tags": ["newsletter", "two-column"],
  "thumbnailUrl": "https://cdn.temway.com/thumbnails/layout_abc123.png",
  "status": "published",
  "updatedAt": "2026-06-26T12:00:00.000Z"
}

Status Codes

Code Description
200 Published layout metadata
401 Missing or invalid API key
403 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.

Path Parameters

Name Type Description
workspaceId string Workspace ID

Request Body

{
  "name": "Newsletter Template",          // required, 1–255 chars
  "description": "Two-column layout",     // optional, max 1000 chars
  "category": "newsletter",               // optional, max 50 chars
  "tags": ["newsletter"],                 // optional
  "canvas": { "width": 600, "height": 800 }, // optional
  "content": { /* layout-editor descriptor */ } // required
}

Status Codes

Code Description
201 Layout created (status: draft)
401 Missing or invalid API key
403 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.

Path Parameters

Name Type Description
workspaceId string Workspace ID
id string Layout ID

Request Body

{
  "name": "New Name",                       // optional
  "description": "...",                     // optional, nullable
  "category": "...",                        // optional, nullable
  "tags": ["..."],                          // optional
  "canvas": { "width": 600, "height": 800 },// optional
  "content": { /* descriptor */ },          // optional
  "thumbnailUrl": "https://..."             // optional, nullable
}

Status Codes

Code Description
200 Layout updated
401 Missing or invalid API key
403 Missing layouts:write scope or not authorized
404 Layout not found in this workspace
422 Malformed request body
429 Rate limit exceeded

Delete Layout

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

Path Parameters

Name Type Description
workspaceId string Workspace ID

Response

{
  "workspaceId": "ws_abc",
  "logoUrl": "https://cdn.temway.com/.../logo.png",
  "primaryColor": "#241A17",
  "fontFamily": "Inter",
  "fromName": "Temway",
  "updatedAt": "2026-06-26T12:00:00.000Z"
}

Status Codes

Code Description
200 Workspace branding
401 Missing or invalid API key
403 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.

Path Parameters

Name Type Description
workspaceId string Workspace ID

Response

[
  {
    "id": "conn_abc123",
    "provider": "mailchimp",
    "name": "Main list",
    "status": "active",        // active | error
    "lastError": null,
    "accountLabel": "Acme Co",
    "createdAt": "2026-06-01T12:00:00.000Z",
    "updatedAt": "2026-06-01T12:00:00.000Z"
  }
]

Status Codes

Code Description
200 Connected ESP accounts for this workspace
401 Missing or invalid API key
403 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
}

Response

{
  "id": "sync_def456",
  "status": "pending",                // pending | processing | completed | failed
  "provider": "mailchimp",
  "templateName": "June Newsletter",
  "providerResourceId": null,
  "providerResourceUrl": null,
  "error": null,
  "emailId": "email_abc",
  "createdAt": "2026-08-10T12:00:00.000Z",
  "updatedAt": "2026-08-10T12:00:00.000Z",
  "completedAt": null
}

Status Codes

Code Description
202 Sync job enqueued
401 Missing or invalid API key
403 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.

Path Parameters

Name Type Description
workspaceId string Workspace ID
id string Email ID

Response

{
  "id": "sync_def456",
  "status": "completed",
  "provider": "mailchimp",
  "templateName": "June Newsletter",
  "providerResourceId": "tpl_123",
  "providerResourceUrl": "https://mailchimp.com/templates/tpl_123",
  "error": null,
  "emailId": "email_abc",
  "createdAt": "2026-08-10T12:00:00.000Z",
  "updatedAt": "2026-08-10T12:00:05.000Z",
  "completedAt": "2026-08-10T12:00:05.000Z"
}

Status Codes

Code Description
200 Latest sync job, or null if none exists
401 Missing or invalid API key
403 Missing espSync:read scope or not authorized
404 Email not found
429 Rate limit exceeded

API Key

Get Current Key

GET /me API Key

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.

Response

[
  { "id": "ws_abc", "name": "Acme Co", "slug": "acme-co", "isDefault": true },
  { "id": "ws_def", "name": "Widgets Inc", "slug": "widgets-inc", "isDefault": false }
]

Status Codes

Code Description
200 Workspaces this key can reach
401 Missing or invalid API key
429 Rate limit exceeded

Quickstart

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.

List published emails

curl https://api.temway.com/api/v1/public/workspaces/{workspaceId}/emails \
  -H "Authorization: Bearer tmw_live_a1b2c3d4e5f67890abcdef12345678"

Create an email, render, and publish

# 1. Create a draft email
curl -X POST https://api.temway.com/api/v1/public/workspaces/{workspaceId}/emails \
  -H "Authorization: Bearer tmw_live_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"June Newsletter","content":{...}}'

# 2. Render the WEB target (publish consumes a web render)
curl -X POST "https://api.temway.com/api/v1/public/workspaces/{workspaceId}/emails/{emailId}/export?target=web" \
  -H "Authorization: Bearer tmw_live_..."
# -> { "jobId": "rjob_xyz", "status": "pending" }

# 3. Poll the render job until status='completed'
curl https://api.temway.com/api/v1/public/workspaces/{workspaceId}/emails/{emailId}/render-jobs/{jobId} \
  -H "Authorization: Bearer tmw_live_..."

# 4. Publish using that render job
curl -X POST https://api.temway.com/api/v1/public/workspaces/{workspaceId}/emails/{emailId}/publish \
  -H "Authorization: Bearer tmw_live_..." \
  -H "Content-Type: application/json" \
  -d '{"renderJobId":"rjob_xyz"}'

Render inbox HTML and test-send

# 1. Render the EMAIL target (test sends consume an email render)
curl -X POST "https://api.temway.com/api/v1/public/workspaces/{workspaceId}/emails/{emailId}/export?target=email" \
  -H "Authorization: Bearer tmw_live_..."
# -> { "jobId": "rjob_abc", "status": "pending" }

# 2. Poll until completed, then test-send
curl -X POST https://api.temway.com/api/v1/public/workspaces/{workspaceId}/emails/{emailId}/test-send \
  -H "Authorization: Bearer tmw_live_..." \
  -H "Content-Type: application/json" \
  -d '{"to":"[email protected]","renderJobId":"rjob_abc"}'
# -> { "jobId": "sjob_def", "status": "pending" }

# 3. Poll the send job
curl https://api.temway.com/api/v1/public/workspaces/{workspaceId}/emails/{emailId}/send-jobs/{jobId} \
  -H "Authorization: Bearer tmw_live_..."

List published layouts

curl https://api.temway.com/api/v1/public/workspaces/{workspaceId}/layouts \
  -H "Authorization: Bearer tmw_live_a1b2c3d4e5f67890abcdef12345678"

Resolve your key

curl https://api.temway.com/api/v1/public/me \
  -H "Authorization: Bearer tmw_live_a1b2c3d4e5f67890abcdef12345678"
# -> { "workspaceId": "ws_abc", "teamId": "team_xyz", "scopes": [...] }