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 are scoped to a specific workspace and carry resource scopes that gate each endpoint. Keys are created and managed from the API Keys 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 — keys cannot access resources in other 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 all four; 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

GET /branding requires emails:read or layouts:read. GET /me requires any valid key (no scope gate — it resolves the key itself).

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

API Key

Get Current Key

GET /me API Key

Resolve the authenticated API key itself. Returns the workspace, team, and scopes the key carries. Not workspace-scoped (mounted without <code>:workspaceId</code>), so it has no scope gate — any valid key resolves itself. The remote MCP server uses this to derive the workspace from a bare key on session init.

Response

{
  "workspaceId": "ws_abc",
  "teamId": "team_xyz",
  "scopes": ["emails:read", "emails:write", "layouts:read", "layouts:write"]
}

Status Codes

Code Description
200 Key identity
401 Missing or invalid API key
429 Rate limit exceeded

Quickstart

API keys are created from the API Keys 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": [...] }