Most endpoints require a Bearer token. Pass it in the Authorization header:
Authorization: Bearer <your-token>
Two token types are supported:
| Token | Prefix | Scope | Use case |
|---|---|---|---|
| API Key | unlayer_sk_ |
Project | Server-to-server integration. The project ID is embedded in the key — no projectId parameter needed. |
| Personal Access Token | unlayer_pat_ |
User | Admin operations. Required for workspace endpoints. Must provide projectId via query param or X-Project-Id header for project endpoints. |
Most integrations should use an API Key. Use a PAT only for workspace management or admin tasks.
The design schema reference at GET /v3/templates/schema is public and does not require authentication.
⚠️ Server-side use only. This API does not send CORS headers and cannot be called from a browser. API keys (
unlayer_sk_*) must never be shipped to client-side code — treat them as secrets.
Get the current plan, feature availability, and limits for a project. Used to answer "can I do X" / "what plan do I need" questions with ground-truth data instead of guessing.
| projectId | string The project ID (required for PAT auth, auto-resolved for API key auth) |
{- "data": {
- "planName": "string",
- "status": "string",
- "expiresAt": "string",
- "features": [
- {
- "name": "string",
- "available": true
}
], - "limits": [
- {
- "name": "string",
- "value": 0,
- "unit": "string"
}
]
}
}Get project details by ID.
| id required | string The resource ID |
{- "data": {
- "id": 0,
- "name": "string",
- "status": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "workspace": {
- "id": 0,
- "name": "string"
}, - "inboxPreviews": {
- "clients": null,
- "dataRegion": "us"
}
}
}Configure the default email clients and data region used for Inbox Previews.
| id required | string The resource ID |
required | object non-empty | ||||
| |||||
{- "inboxPreviews": {
- "clients": [
- "string"
], - "dataRegion": "us"
}
}{- "data": {
- "inboxPreviews": {
- "clients": null,
- "dataRegion": "us"
}
}
}List templates with cursor-based pagination. Returns templates in descending order by update time.
| projectId | string The project ID to list templates for |
| limit | integer [ 1 .. 100 ] Default: 20 Number of templates to return (1-100) |
| cursor | string Pagination cursor from previous response |
| displayMode | string Enum: "email" "web" "document" Filter by template type |
| name | string Filter by name (case-insensitive search) |
{- "data": [
- {
- "id": "string",
- "name": "string",
- "displayMode": "email",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}
], - "next_cursor": "string",
- "has_more": true
}Get template by ID.
| id required | string The resource ID |
| projectId | string The project ID (required for PAT auth, auto-resolved for API key auth) |
{- "data": {
- "id": "string",
- "name": "string",
- "displayMode": "email",
- "design": { },
- "html": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}
}Convert design json from Full to Simple schema.
required | object |
| displayMode | string Default: "email" Enum: "email" "web" "popup" "document" Display mode of the design (email, web, document, popup). Defaults to "email", matching /v3/templates/validate. Mode-specific repairs apply during conversion (email caps contentWidth at 900px, for example), so pass the design's actual mode — a web design converted under the email default can be altered. |
| includeDefaultValues | boolean Default: false |
| includeConversion | boolean Default: false When true, includes _conversion metadata in the response. This metadata can be passed to simple-to-full to restore original values without data loss. |
{- "design": {
- "body": { },
- "counters": { },
- "schemaVersion": 0
}, - "displayMode": "email",
- "includeDefaultValues": false,
- "includeConversion": false
}{- "success": true,
- "data": {
- "design": { }
}
}Convert design json from Simple to Full schema.
required | object |
| displayMode | string Default: "email" Enum: "email" "web" "popup" "document" Display mode of the design (email, web, document, popup). Defaults to "email", matching /v3/templates/validate. Mode-specific repairs apply during conversion (email caps contentWidth at 900px, for example), so pass the design's actual mode — a web design converted under the email default can be altered. |
| includeDefaultValues | boolean Default: false |
{- "design": {
- "body": { },
- "counters": { },
- "schemaVersion": 0,
- "_conversion": {
- "data": "string",
- "version": 0
}
}, - "displayMode": "email",
- "includeDefaultValues": false
}{- "success": true,
- "data": {
- "design": { }
}
}Generate or modify an Unlayer design using AI. Send the conversation as messages (today only the last user message is consumed; earlier turns are accepted as chat history) and describe the target with output.kind + output.displayMode. Pass the current canvas state in context (full design JSON + selection pointer) to modify an existing design. Only anthropic and openai models are supported. To import existing HTML or an image instead, use POST /v3/templates/import.
| projectId | string The project ID (required for PAT auth, auto-resolved for API key auth) |
| model | string Preferred AI model in "provider/id" form, e.g. "anthropic/claude-opus-5". Optional — server resolves a default per output kind. |
boolean or Array of strings Transient-outage fallback controls. Omit to use Unlayer defaults only when no model is pinned; true always uses Unlayer defaults; false disables the outage tail; an ordered array replaces the default provider/model strings. | |
required | Array of objects [ 1 .. 10 ] items Conversation messages in chronological order, capped at 10 messages. The last |
required | object |
object | |
| locale | string <= 100 characters ^[A-Za-z0-9]{1,8}(?:-[A-Za-z0-9]{1,8})*$ BCP-47 fallback locale for AI status messages. |
| conversationId | string Reserved for future server-side conversation memory. |
{- "model": "string",
- "fallbackModels": true,
- "messages": [
- {
- "role": "user",
- "content": [
- {
- "type": "text",
- "text": "string",
- "image": "string",
- "file": {
- "url": "string",
- "mediaType": "string"
}
}
], - "metadata": {
- "action": {
- "id": "string"
}
}
}
], - "output": {
- "kind": "template",
- "displayMode": "email",
- "schemaVersion": 0
}, - "context": {
- "fullDesign": { },
- "selection": {
- "collection": "pages",
- "id": "string",
- "value": "string"
}, - "availableTools": [
- "string"
], - "availableFonts": [
- {
- "label": "string",
- "value": "string"
}
], - "customTools": [
- {
- "slug": "string",
- "options": { }
}
], - "brand": {
- "companyName": "string",
- "productDescription": "string",
- "targetAudience": "string",
- "colors": {
- "primary": "string",
- "secondary": "string",
- "accent": "string"
}, - "fonts": {
- "heading": "string",
- "body": "string"
}, - "voice": "string",
- "guidelines": "string"
}
}, - "locale": "string",
- "conversationId": "string"
}{- "id": "string",
- "output": {
- "kind": "string",
- "data": { }
}, - "model": {
- "provider": "string",
- "id": "string"
}, - "usage": {
- "inputTokens": 0,
- "outputTokens": 0,
- "totalTokens": 0,
- "cachedInputTokens": 0,
- "reasoningTokens": 0,
- "aiCreditsUsed": 0,
- "estimatedCostMicroUsd": 0
}
}Import an existing template from HTML or an image (URL or base64) and return the resulting Unlayer design JSON. No template DB entry is created.
| projectId | string The project ID (required for PAT auth, auto-resolved for API key auth) |
| model | string <= 200 characters Preferred AI model. Accepts a provider/model string (e.g. "anthropic/claude-opus-5", "openai/gpt-5.6-luna"), a bare provider ("anthropic", "openai") which uses that provider's default model, or a bare model id ("claude-opus-5", "gpt-5.6-luna") with the provider inferred from the name. Optional — defaults to anthropic/claude-opus-5. |
boolean or Array of strings Transient-outage fallback controls. Omit to use Unlayer defaults only when no model is pinned; true always uses Unlayer defaults; false disables the outage tail; an ordered array replaces the default provider/model strings. | |
| displayMode required | string Enum: "email" "web" "popup" "document" Display mode for the imported design |
required | Array of objects [ 1 .. 10 ] items Array of input parts. Must contain exactly one "html" or "image" part; may also contain one or more "text" parts with optional instructions. |
{- "model": "string",
- "fallbackModels": true,
- "displayMode": "email",
- "input": [
- {
- "type": "html",
- "html": "string",
- "url": "string",
- "data": "string",
- "text": "string"
}
]
}{- "id": "string",
- "output": {
- "type": "string",
- "blockType": "string",
- "data": { }
}, - "model": "string",
- "provider": "string",
- "usage": {
- "inputTokens": 0,
- "outputTokens": 0,
- "totalTokens": 0,
- "reasoningTokens": 0,
- "cachedInputTokens": 0
}
}Returns the canonical design schema as a standard JSON Schema document — the exact schema POST /v3/templates/validate checks against, ready to plug into any JSON Schema validator or editor tooling. Serves the Full schema by default; pass simple=true for the compact Simple schema. No authentication required. Responses carry a strong ETag and long-lived cache headers; send If-None-Match to revalidate for free.
| simple | boolean Default: false When true, returns the Simple schema instead of the Full schema. |
| displayMode | string Default: "email" Enum: "email" "web" "popup" "document" Display mode whose rules the schema describes (email, web, document, popup). Defaults to "email". |
Validate a design JSON against the Unlayer design schema. Returns { success: true, data: { valid: true } } when the payload conforms; otherwise data is { valid: false, errors: [...] } with descriptive issues. Every checked design gets HTTP 200 — data.valid is the source of truth, not the status code. Only malformed requests (e.g. a missing design field or an unknown displayMode) fail request validation with 400 VALIDATION_ERROR.
required | object The design JSON to validate. |
| schema | string Default: "full" Enum: "full" "simple" Which form of the schema to validate against. Defaults to "full". |
| displayMode | string Default: "email" Enum: "email" "web" "popup" "document" Display mode for the design (email, web, document, popup). Some validation rules differ per mode. Defaults to "email" — without a default, options from every mode would apply at once, the strictest possible check, and real editor-saved designs could be reported invalid. |
| migrate | boolean Default: true When true (default), a full-form design with an older schemaVersion is upgraded to the current schema before validating — matching how the editor and the convert endpoints treat stored designs. Designs without a schemaVersion predate versioning and are fully migrated the same way. Set to false to check strict conformance with the current schema version. Designs with a newer schemaVersion than this API knows are validated as-if-current. |
Array of objects <= 100 items Custom tool declarations, in the same shape passed to unlayer.registerTool. When provided, blocks matching a declared tool have their values checked against the tool's declared options (wrong types are reported at their exact path). Blocks of undeclared tools keep envelope-only validation. |
{- "design": { },
- "schema": "full",
- "displayMode": "email",
- "migrate": true,
- "customTools": [
- {
- "slug": "string",
- "type": "custom",
- "label": "string",
- "options": {
- "property1": {
- "options": { }
}, - "property2": {
- "options": { }
}
}, - "values": { },
- "supportedDisplayModes": [
- "email"
]
}
]
}{- "success": true,
- "data": {
- "valid": true,
- "migratedFrom": 0,
- "errors": [
- {
- "path": "string",
- "message": "string",
- "code": "string"
}
], - "errorCount": 0
}
}Get a specific workspace by ID with its projects. Requires a Personal Access Token (PAT).
| workspaceId required | string The workspace ID |
{- "data": {
- "id": 0,
- "name": "string",
- "projects": [
- {
- "id": 0,
- "name": "string",
- "status": "string"
}
]
}
}Reusable design blocks — list shared project blocks and end-user saved blocks for backup, migration, and usage reporting.
List blocks with cursor-based pagination. Returns both shared project blocks and blocks saved by end-users; each user-saved block carries the userId it was saved under (null for shared blocks), so usage can be aggregated per end-user without enumerating user IDs. Returns blocks in descending order by creation.
| projectId | string The project ID to list blocks for |
| limit | integer [ 1 .. 100 ] Default: 20 Number of blocks to return (1-100) |
| cursor | string Pagination cursor from previous response |
| displayMode | string Enum: "email" "web" "popup" "document" Filter by display mode |
| userId | string Only blocks saved by this end-user (exact match on the user id your app passes to the editor) |
| scope | string Default: "all" Enum: "all" "shared" "user" Filter by block ownership: shared project blocks, end-user saved blocks, or both |
| category | string Filter by category (case-insensitive search) |
| includeData | boolean Default: true Include the block design JSON in each item. Pass false for lightweight sweeps (e.g. usage reports). |
{- "data": [
- {
- "id": "string",
- "userId": "string",
- "displayMode": "string",
- "category": "string",
- "tags": [
- "string"
], - "data": { },
- "thumbnailUrl": "string",
- "syncId": "string",
- "isSyncEnabled": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}
], - "next_cursor": "string",
- "has_more": true
}AI credit balance, usage breakdown, and webhook/alert settings. Credits are pooled per workspace; settings are per project.
Returns the current AI credit balance for the project. Credits are pooled per workspace — every project in a workspace shares one balance. Only credit counts are returned; token counts, model names, and costs are never exposed.
| id required | string The resource ID |
{- "is_capped": true,
- "credits_total": 0,
- "credits_used": 0,
- "credits_remaining": 0,
- "reset_date": null
}Returns a project's AI credit exhaustion behavior, alert thresholds, and webhook endpoint. The signing secret is never returned — only whether one exists (has_signing_secret).
| id required | string The resource ID |
{- "exhaustion_behavior": "disable",
- "threshold_alerts": [
- 0
], - "webhook_url": null,
- "has_signing_secret": true
}Configures AI credit exhaustion behavior, usage alert thresholds, and the webhook endpoint for a project. The HMAC signing secret is generated the first time a webhook URL is set and returned exactly once in the response — store it securely; it is never shown again.
| id required | string The resource ID |
| exhaustion_behavior | string Enum: "disable" "show_error" What the editor does when the credit balance is exhausted. |
| threshold_alerts | Array of integers[ items [ 1 .. 100 ] ] Usage percentages (1-100) at which a threshold_reached webhook fires, once per crossing per period. |
| webhook_url | string or null <uri> ^https:// HTTPS endpoint that receives AI credit webhooks. |
{- "exhaustion_behavior": "disable",
- "threshold_alerts": [
- 1
],
}{- "exhaustion_behavior": "disable",
- "threshold_alerts": [
- 0
], - "webhook_url": null,
- "has_signing_secret": true,
- "signing_secret": "string"
}Generates a new HMAC signing secret for the project and returns it exactly once. The previous secret stops working immediately, so update your webhook verification before rotating. Requires a webhook URL to be configured first.
| id required | string The resource ID |
{- "signing_secret": "string"
}Returns AI credit consumption for the project, broken down by end user and feature type. Filterable by date range, end user, and feature type. Usage is updated near real time and grouped by the UTC date when the AI activity occurred. Recent activity may take a short time to appear. Defaults to the current billing period. Only credit counts are returned; token counts, model names, and costs are never exposed. Per-end-user attribution requires the partner to pass endUserId on editor initialization.
| id required | string The resource ID |
| start | string^\d{4}-\d{2}-\d{2}$ Start date (inclusive), YYYY-MM-DD. |
| end | string^\d{4}-\d{2}-\d{2}$ End date (inclusive), YYYY-MM-DD. |
| end_user_id | string Filter to a single end user id. |
| feature_type | string Enum: "full_template_gen" "block_edit" "html_import" "image_import" "image_generation" Filter to a single feature type. |
| limit | integer [ 1 .. 1000 ] Default: 100 Max breakdown rows to return (1-1000). |
| offset | integer >= 0 Default: 0 Number of breakdown rows to skip (pagination). |
| sort | string Default: "credits" Enum: "credits" "end_user_id" "feature_type" Field the breakdown is ordered by. Defaults to credits. |
| order | string Default: "desc" Enum: "asc" "desc" Sort direction. Defaults to desc (highest credits first). |
{- "total_credits_used": 0,
- "total": 0,
- "breakdown": [
- {
- "end_user_id": null,
- "feature_type": "full_template_gen",
- "credits": 0
}
]
}Returns the webhook delivery history for the project, newest first — the event, delivery status, attempt count, and last response code for each. Use it to spot failed deliveries and drive the retry endpoint. Payloads expose credits only.
| id required | string The resource ID |
| status | string Enum: "pending" "delivered" "failed" Filter to a single delivery status. |
| event | string Enum: "ai.credits.usage_recorded" "ai.credits.threshold_reached" "ai.credits.exhausted" Filter to a single event type. |
| limit | integer [ 1 .. 100 ] Default: 50 Max deliveries to return (1-100). |
| offset | integer >= 0 Default: 0 Number of deliveries to skip (pagination). |
{- "deliveries": [
- {
- "id": "string",
- "event": "ai.credits.usage_recorded",
- "status": "pending",
- "attempts": 0,
- "last_status_code": null,
- "end_user_id": null,
- "created_at": "2019-08-24T14:15:22Z",
- "delivered_at": null,
- "payload": { }
}
], - "total": 0
}Returns the per-attempt history for a single delivery, newest attempt first — the response code, error, and time of each POST (including automatic retries). Returns 404 if the delivery is not found for this project.
| id required | string The project ID |
| deliveryId required | string The webhook delivery ID |
| limit | integer [ 1 .. 100 ] Default: 50 Max attempts to return (1-100). |
| offset | integer >= 0 Default: 0 Number of attempts to skip (pagination). |
{- "attempts": [
- {
- "attempt": 0,
- "status_code": null,
- "error": null,
- "attempted_at": "2019-08-24T14:15:22Z"
}
], - "total": 0
}Re-queues a single previously-failed (or pending) webhook delivery for another attempt. Returns 404 if the delivery is not found for this project, and 409 if it was already delivered.
| id required | string The project ID |
| deliveryId required | string The webhook delivery ID |
{- "status": "requeued"
}Returns project credit totals, a daily trend, and an end-user breakdown. The current billing model uses one credit per selected client; previous-model generations use one credit per accepted LIVE run and none for TEST runs.
| id required | string The resource ID |
| start | string^\d{4}-\d{2}-\d{2}$ Start date (inclusive), YYYY-MM-DD. |
| end | string^\d{4}-\d{2}-\d{2}$ End date (inclusive), YYYY-MM-DD. |
| end_user_id | string [ 1 .. 10000 ] characters Only include usage attributed to this end user ID. It is normalized exactly as it was when the usage was recorded. |
| limit | integer [ 1 .. 100 ] Default: 100 Maximum rows to return. |
| offset | integer [ 0 .. 100000 ] Default: 0 Number of rows to skip. |
{- "summary": {
- "total_generations": 0,
- "per_device_generations": 0,
- "per_generation_generations": 0,
- "total_credits": 0,
- "per_device_credits": 0,
- "per_generation_credits": 0,
- "per_generation_credit_details_unavailable": 0,
- "total_device_credits": 0,
- "attributed_end_users": 0,
- "unattributed_generations": 0
}, - "daily": [
- {
- "date": "2019-08-24",
- "generations": 0,
- "per_device_generations": 0,
- "per_generation_generations": 0,
- "credits": 0,
- "per_device_credits": 0,
- "per_generation_credits": 0,
- "per_generation_credit_details_unavailable": 0,
- "device_credits": 0
}
], - "email_clients": [
- {
- "client_id": "string",
- "generations": 0,
- "device_credits": 0
}
], - "end_users": {
- "total": 0,
- "limit": 0,
- "offset": 0,
- "data": [
- {
- "end_user_id": null,
- "generations": 0,
- "device_credits": 0
}
]
}
}Permanently removes one end user ID from this project’s Inbox Previews usage history while preserving generation and credit totals.
| id required | string The resource ID |
| end_user_id required | string [ 1 .. 10000 ] characters The original end user ID supplied to the editor. It is normalized exactly as it was when usage was recorded. |
{- "end_user_id": "string"
}{- "error": "string",
- "message": "string"
}Returns recent project generations and their credit usage. Per-device rows include the selected clients and their view status; per-generation rows list the clients the provider reported, without view, end-user, template, or region details.
| id required | string The resource ID |
| start | string^\d{4}-\d{2}-\d{2}$ Start date (inclusive), YYYY-MM-DD. |
| end | string^\d{4}-\d{2}-\d{2}$ End date (inclusive), YYYY-MM-DD. |
| end_user_id | string [ 1 .. 10000 ] characters Only include usage attributed to this end user ID. It is normalized exactly as it was when the usage was recorded. |
| limit | integer [ 1 .. 100 ] Default: 25 Maximum rows to return. |
| offset | integer [ 0 .. 100000 ] Default: 0 Number of rows to skip. |
{- "total": 0,
- "limit": 0,
- "offset": 0,
- "data": [
- {
- "id": "string",
- "generated_at": "2019-08-24T14:15:22Z",
- "billing_model": "per-device",
- "end_user_id": null,
- "template_id": null,
- "clients": [
- "string"
], - "viewed_clients": null,
- "automatically_viewed_clients": null,
- "interactively_viewed_clients": null,
- "click_viewed_clients": null,
- "navigation_viewed_clients": null,
- "credits_used": null,
- "credit_details_available": true,
- "per_device_credits": 0,
- "per_generation_credits": null,
- "device_credits": null,
- "data_region": "us",
- "configuration_source": "editor"
}
]
}Create an ephemeral, no-DB editor session for a design and return a hosted editor URL the user can open to edit it in the real Unlayer editor.
| projectId | string The project ID (required for PAT auth, auto-resolved for API key auth) |
required | object Design JSON to load into the editor. |
| displayMode | string Enum: "email" "web" "popup" "document" Editor display mode. Defaults to email. |
{- "design": { },
- "displayMode": "email"
}{- "data": {
- "token": "string",
- "editorUrl": "string",
- "expiresAt": "string"
}
}Export a design as rendered HTML.
| projectId | string The project ID (required for PAT auth, auto-resolved for API key auth) |
| design required | object Unlayer design JSON |
| displayMode | string Enum: "email" "web" "popup" "document" |
| customJS | Array of strings or string |
| editorVersion | string |
| mergeTags | object |
| mergeTagsSchema | object |
| designTags | object |
| designTagsConfig | object |
| safeHtml | boolean |
| language | string |
| languages | Array of strings |
{- "design": { },
- "displayMode": "email",
- "customJS": "string",
- "editorVersion": "string",
- "mergeTags": { },
- "mergeTagsSchema": { },
- "designTags": { },
- "designTagsConfig": { },
- "safeHtml": true,
- "language": "string",
- "languages": [
- "string"
]
}{- "success": true,
- "data": {
- "html": "string",
- "chunks": {
- "css": "string",
- "js": "string",
- "body": "string",
- "fonts": [ ],
- "tags": [
- "string"
]
}, - "design": { },
- "amp": { }
}
}Export a design as a PNG image.
| projectId | string The project ID (required for PAT auth, auto-resolved for API key auth) |
| design required | object Unlayer design JSON |
| displayMode | string Enum: "email" "web" "popup" "document" |
| customJS | Array of strings or string |
| editorVersion | string |
| mergeTags | object |
| mergeTagsSchema | object |
| designTags | object |
| designTagsConfig | object |
| safeHtml | boolean |
| language | string |
| languages | Array of strings |
| width | number |
| height | number |
| fullPage | boolean |
| deviceScaleFactor | number |
{- "design": { },
- "displayMode": "email",
- "customJS": "string",
- "editorVersion": "string",
- "mergeTags": { },
- "mergeTagsSchema": { },
- "designTags": { },
- "designTagsConfig": { },
- "safeHtml": true,
- "language": "string",
- "languages": [
- "string"
], - "width": 0,
- "height": 0,
- "fullPage": true,
- "deviceScaleFactor": 0
}{- "success": true,
- "data": {
- "url": "string"
}
}Export a design as a PDF document.
| projectId | string The project ID (required for PAT auth, auto-resolved for API key auth) |
| design required | object Unlayer design JSON |
| displayMode | string Enum: "email" "web" "popup" "document" |
| customJS | Array of strings or string |
| editorVersion | string |
| mergeTags | object |
| mergeTagsSchema | object |
| designTags | object |
| designTagsConfig | object |
| safeHtml | boolean |
| language | string |
| languages | Array of strings |
| pageSize | string Enum: "Letter" "Legal" "Tabloid" "Ledger" "A0" "A1" "A2" "A3" "A4" "A5" "A6" |
number or string |
{- "design": { },
- "displayMode": "email",
- "customJS": "string",
- "editorVersion": "string",
- "mergeTags": { },
- "mergeTagsSchema": { },
- "designTags": { },
- "designTagsConfig": { },
- "safeHtml": true,
- "language": "string",
- "languages": [
- "string"
], - "pageSize": "Letter",
- "contentWidth": 0
}{- "success": true,
- "data": {
- "url": "string"
}
}Export a design as a ZIP archive containing HTML and assets.
| projectId | string The project ID (required for PAT auth, auto-resolved for API key auth) |
| design required | object Unlayer design JSON |
| displayMode | string Enum: "email" "web" "popup" "document" |
| customJS | Array of strings or string |
| editorVersion | string |
| mergeTags | object |
| mergeTagsSchema | object |
| designTags | object |
| designTagsConfig | object |
| safeHtml | boolean |
| language | string |
| languages | Array of strings |
{- "design": { },
- "displayMode": "email",
- "customJS": "string",
- "editorVersion": "string",
- "mergeTags": { },
- "mergeTagsSchema": { },
- "designTags": { },
- "designTagsConfig": { },
- "safeHtml": true,
- "language": "string",
- "languages": [
- "string"
]
}{- "success": true,
- "data": {
- "url": "string"
}
}List emails sent from this project within the rolling 90-day history window. Without a status filter, results and date bounds use acceptance time. With a status filter, results and date bounds use the time each email entered that status.
| projectId | string Project ID (auto-resolved for API key auth) |
| status | string Enum: "queued" "sending" "sent" "delivered" "bounced" "complained" "failed" Filter by email delivery status |
| search | string Search recipient addresses and subjects by case-sensitive substring |
| tag | string Filter by tag in "key=value" format (e.g. "campaign=welcome") |
| from | string <date> Start date (ISO date). Bounds acceptance time normally, or status transition time when status is supplied. |
| to | string <date> End date (ISO date). Bounds acceptance time normally, or status transition time when status is supplied. |
| limit | integer [ 1 .. 100 ] Default: 20 Number of emails to return (1-100) |
| cursor | string Pagination cursor from previous response |
{- "data": [
- {
- "id": "string",
- "from": "string",
- "to": null,
- "subject": "string",
- "status": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "statusUpdatedAt": "2019-08-24T14:15:22Z"
}
], - "next_cursor": "string",
- "has_more": true
}Send a transactional email with raw HTML content. The sender domain must be verified in the project workspace; verified sender domains are shared by every Developer Email API project in that workspace.
| idempotency-key | string <= 255 characters Unique key for idempotent sends (max 255 characters). If provided, duplicate requests within 24 hours return the cached response. |
| from required | string Sender email address or "Name |
| to required | Array of strings <email> = 1 items [ items <email > ] Exactly one recipient. Each request creates one independently tracked delivery. |
| cc | Array of arrays <= 0 items CC is not supported by this endpoint. |
| bcc | Array of arrays <= 0 items BCC is not supported by this endpoint. |
| subject required | string <= 998 characters Email subject line |
| html required | string HTML content of the email |
| text | string Plain text version of the email. If provided, a multipart/alternative message is sent. |
| replyTo | string <email> Reply-To email address |
object <= 10 properties Key-value tags for categorizing the email (e.g. {"campaign": "welcome"}). Max 10 tags. Keys (1-64 chars) and values (up to 256 chars) may only contain letters, numbers, underscores, and hyphens (the Amazon SES message-tag character set). | |
object <= 9 properties Custom email headers. Up to 9 printable-ASCII X-* headers are allowed (e.g. {"X-Entity-Ref-ID": "abc123"}). Header names may contain up to 126 characters and each name plus value may contain up to 996 characters. | |
Array of objects <= 10 items File attachments. Max 10 files per email, max 5 MB total payload size (including headers and base64 overhead). |
{- "from": "string",
- "to": [
- "user@example.com"
], - "cc": [ ],
- "bcc": [ ],
- "subject": "string",
- "html": "string",
- "text": "string",
- "replyTo": "user@example.com",
- "tags": {
- "property1": "string",
- "property2": "string"
}, - "headers": {
- "property1": "string",
- "property2": "string"
}, - "attachments": [
- {
- "filename": "string",
- "content": "string",
- "contentType": "application/pdf"
}
]
}{- "data": {
- "id": "string",
- "from": "string",
- "to": [
- "string"
], - "subject": "string",
- "status": "queued",
- "createdAt": "2019-08-24T14:15:22Z"
}
}Retrieve details of a sent email, including its current delivery status, during the rolling 90-day history window. Expired emails return 404.
| id required | string Email ID |
{- "data": {
- "id": "string",
- "from": "string",
- "to": null,
- "cc": [
- "string"
], - "bcc": [
- "string"
], - "subject": "string",
- "status": "string",
- "failureReason": "string",
- "tags": {
- "property1": "string",
- "property2": "string"
}, - "createdAt": "2019-08-24T14:15:22Z"
}
}Retrieve the operational event timeline for a sent email, showing send, delivery, bounce, and complaint events in chronological order during the rolling 90-day history window. Expired emails return 404.
| id required | string Email ID |
{- "data": [
- {
- "type": "string",
- "timestamp": "2019-08-24T14:15:22Z",
- "metadata": { }
}
]
}Render a saved email template with optional merge variables. Returns the final HTML without sending. Useful for previewing emails before sending.
| templateId required | string^[1-9][0-9]*$ Template ID to render |
object <= 100 properties Merge variables to substitute. Use {{key}} syntax in your template. |
{- "templateId": "string",
- "variables": {
- "property1": "string",
- "property2": "string"
}
}{- "data": {
- "html": "string",
- "subject": "string"
}
}Update the email sending configuration for this project. Only include the fields you want to change.
| defaultFromName | string <= 255 characters Default sender display name |
{- "defaultFromName": "string"
}{- "data": {
- "defaultFromName": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}
}Get aggregated email delivery statistics for a project. Returns totals or daily breakdown for the specified period. Statistics are asynchronous and may lag by about one hour.
| projectId | string Project ID (auto-resolved for API key auth) |
| period | string Default: "30d" Enum: "7d" "30d" "90d" Time period for stats |
| groupBy | string Value: "day" Group results by day for chart data |
{- "data": {
- "period": "7d",
- "sent": 0,
- "delivered": 0,
- "bounced": 0,
- "complained": 0,
- "deliveryRate": 0,
- "bounceRate": 0
}
}List all email addresses suppressed for this project due to bounces, complaints, or manual suppression. Cursor-paginated.
| projectId | string Project ID (auto-resolved for API key auth) |
| limit | integer [ 1 .. 200 ] Default: 100 Max number of results (1-200) |
| cursor | string Pagination cursor from a previous response. Omit to start from the beginning. |
{- "data": [
- {
- "email": "string",
- "reason": "hard_bounce",
- "createdAt": "2019-08-24T14:15:22Z"
}
], - "has_more": true,
- "next_cursor": null
}Manually add an email address to the suppression list. Future sends to this address will be blocked.
| email required | string <email> Email address to suppress |
{- "email": "user@example.com"
}{- "data": {
- "email": "string",
- "reason": "string",
- "createdAt": "2019-08-24T14:15:22Z"
}
}Remove an email address from the suppression list so it can receive emails again.
| projectId | string Project ID (auto-resolved for API key auth) |
| email required | string Email address to unsuppress |
{- "data": {
- "email": "string",
- "removed": true
}
}Look up a specific email address to see if it is currently on the suppression list.
| projectId | string Project ID (auto-resolved for API key auth) |
| email required | string Email address to check |
{- "data": {
- "email": "string",
- "suppressed": true
}
}Send a transactional email by rendering a saved template with optional merge variables. The template must have rendered HTML (saved at least once in the editor). The sender domain must be verified in the project workspace; verified sender domains are shared by every Developer Email API project in that workspace.
| idempotency-key | string <= 255 characters Unique key for idempotent sends (max 255 characters). Duplicate requests within 24 hours return the cached response. |
| from required | string Sender email address or "Name |
| to required | Array of strings <email> = 1 items [ items <email > ] Exactly one recipient. Each request creates one independently tracked delivery. |
| cc | Array of arrays <= 0 items CC is not supported by this endpoint. |
| bcc | Array of arrays <= 0 items BCC is not supported by this endpoint. |
| templateId required | string^[1-9][0-9]*$ Template ID to use for the email body |
| subject | string <= 998 characters Email subject line. Supports {{variable}} merge syntax. Defaults to template name if omitted. |
object <= 100 properties Merge variables to substitute in the template and subject. Use {{key}} syntax in your template. | |
| text | string Plain text version of the email. Supports {{variable}} merge syntax. |
| replyTo | string <email> Reply-To email address |
object <= 10 properties Key-value tags for categorizing the email (e.g. {"campaign": "welcome"}). Max 10 tags. Keys (1-64 chars) and values (up to 256 chars) may only contain letters, numbers, underscores, and hyphens (the Amazon SES message-tag character set). | |
object <= 9 properties Custom email headers. Up to 9 printable-ASCII X-* headers are allowed. Header names may contain up to 126 characters and each name plus value may contain up to 996 characters. | |
Array of objects <= 10 items File attachments. Max 10 files per email, max 5 MB total payload size. |
{- "from": "string",
- "to": [
- "user@example.com"
], - "cc": [ ],
- "bcc": [ ],
- "templateId": "string",
- "subject": "string",
- "variables": {
- "property1": "string",
- "property2": "string"
}, - "text": "string",
- "replyTo": "user@example.com",
- "tags": {
- "property1": "string",
- "property2": "string"
}, - "headers": {
- "property1": "string",
- "property2": "string"
}, - "attachments": [
- {
- "filename": "string",
- "content": "string",
- "contentType": "application/pdf"
}
]
}{- "data": {
- "id": "string",
- "from": "string",
- "to": [
- "string"
], - "subject": "string",
- "status": "queued",
- "createdAt": "2019-08-24T14:15:22Z"
}
}List sender domains shared by every Developer Email API project in the workspace. Requires a personal access token belonging to a workspace owner or admin; project API keys cannot manage domains.
{- "data": [
- {
- "id": 0,
- "domain": "string",
- "status": "pending",
- "createdAt": "2019-08-24T14:15:22Z"
}
]
}Register a sender domain shared by every Developer Email API project in the workspace. Requires a personal access token belonging to a workspace owner or admin. Verification requires the workspace-specific TXT record and the returned SES DKIM records.
| domain required | string Domain name to register, such as example.com. |
{- "domain": "string"
}{- "data": {
- "id": 0,
- "domain": "string",
- "status": "pending",
- "createdAt": "2019-08-24T14:15:22Z",
- "dkimTokens": [
- "string"
], - "dnsRecords": [
- {
- "type": "string",
- "name": "string",
- "value": "string",
- "purpose": "string"
}
]
}
}Get the ownership TXT challenge and SES DKIM records for a sender domain shared by every Developer Email API project in the workspace. Requires a personal access token belonging to a workspace owner or admin.
| id required | string Domain ID |
{- "data": {
- "id": 0,
- "domain": "string",
- "status": "string",
- "dkimTokens": [
- "string"
], - "dnsRecords": [
- {
- "type": "string",
- "name": "string",
- "value": "string",
- "purpose": "string"
}
], - "createdAt": "2019-08-24T14:15:22Z"
}
}Delete a sender domain shared by every Developer Email API project in the workspace. Requires a personal access token belonging to a workspace owner or admin. The SES identity remains so a later reconciler can clean it up safely.
| id required | string Domain ID |
{- "data": {
- "success": true
}
}Verify the ownership TXT challenge and SES DKIM identity for a sender domain shared by every Developer Email API project in the workspace. Requires a personal access token belonging to a workspace owner or admin.
| id required | string Domain ID |
{- "data": {
- "id": 0,
- "domain": "string",
- "status": "string",
- "ownership": {
- "verified": true
}, - "dkim": {
- "status": "string",
- "tokens": [
- "string"
]
}
}
}Create a new webhook endpoint. A signing secret is auto-generated and returned once. Use it to verify webhook signatures.
| url required | string <uri> The HTTPS URL to receive webhook events |
| events | Array of strings Items Enum: "email.sent" "email.delivered" "email.bounced" "email.complained" Event types to subscribe to. If omitted or empty, all events are sent. |
| active | boolean Default: true Whether the webhook is active |
{- "events": [
- "email.sent"
], - "active": true
}{- "data": {
- "id": 0,
- "url": "string",
- "events": [
- "email.sent"
], - "active": true,
- "secret": "string",
- "createdAt": "2019-08-24T14:15:22Z"
}
}Get details of a specific webhook endpoint.
| id required | string Webhook ID |
{- "data": {
- "id": 0,
- "url": "string",
- "events": [
- "email.sent"
], - "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}
}Update a webhook endpoint URL, events, or active status.
| id required | string Webhook ID |
| url | string <uri> The HTTPS URL to receive webhook events |
| events | Array of strings Items Enum: "email.sent" "email.delivered" "email.bounced" "email.complained" Event types to subscribe to. If omitted or empty, all events are sent. |
| active | boolean Whether the webhook is actively receiving events |
{- "events": [
- "email.sent"
], - "active": true
}{- "data": {
- "id": 0,
- "url": "string",
- "events": [
- "email.sent"
], - "active": true,
- "updatedAt": "2019-08-24T14:15:22Z"
}
}Generate a new signing secret for a webhook. The new secret is returned once — store it securely. The old secret is invalidated immediately.
| id required | string Webhook ID |
{- "data": {
- "id": 0,
- "secret": "string",
- "updatedAt": "2019-08-24T14:15:22Z"
}
}