Email designs API
REST endpoints for email designs in SKUFlow: 14 operations with parameters, request and response fields, permissions and curl examples.
Base URL: https://app.skuflow.ai/api. Every call needs Authorization: Bearer skf_… (Authentication); {organizationId} ($ORGANIZATION_ID in the examples) is your business's id.
| Operation | Endpoint | Risk |
|---|---|---|
| Get the email theme | GET /organizations/{organizationId}/messaging/email-theme | read |
| Change the email theme | PATCH /organizations/{organizationId}/messaging/email-theme | write |
| List the email designs | GET /organizations/{organizationId}/email-designs | read |
| Get an email template's design | GET /organizations/{organizationId}/message-templates/{templateId}/design | read |
| Save an email template's design | PUT /organizations/{organizationId}/message-templates/{templateId}/design | write |
| Reset an email template's design | DELETE /organizations/{organizationId}/message-templates/{templateId}/design | sensitive |
| Preview an email design | POST /organizations/{organizationId}/email-designs/preview | write |
| Send a test of an email design | POST /organizations/{organizationId}/email-designs/test | write |
| Draft an email theme with AI | POST /organizations/{organizationId}/email-designs/ai/theme | write |
| Draft an email template's design with AI | POST /organizations/{organizationId}/message-templates/{templateId}/design/draft | write |
| The image library for emails | GET /organizations/{organizationId}/email-designs/media | read |
| Upload an email image | POST /organizations/{organizationId}/email-designs/image-upload-url | write |
| Add an uploaded email image to the library | POST /organizations/{organizationId}/email-designs/media/uploads | write |
| Add an image for emails (content in the request) | POST /organizations/{organizationId}/email-designs/media/content | write |
Get the email theme
GET /organizations/{organizationId}/messaging/email-theme
The brand theme every email shares (Settings → Messaging → Email theme): page, body and header backgrounds, text, link and button colours and button style, font, logo size (small / medium / large or a width in px) and alignment, the accent bar, a banner image, social links, the accent colour, the line under the logo and the footer text. Read-only.
| Permission | engagement.read (Owner, Admin, Staff) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | emailDesigns_getTheme (toolset marketing) |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/messaging/email-theme" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
pageBackground | string | |
cardBackground | string | |
headerBackground | string | |
textColor | string | |
linkColor | string | |
buttonColor | string or null | null = the accent colour. |
buttonTextColor | string | |
buttonStyle | enum | One of filled, outline. |
buttonRadius | integer | |
fontFamily | string | |
logoSize | enum | One of small, medium, large, custom. |
logoWidth | integer | |
logoAlign | enum | One of left, center. |
showBrandBar | boolean | |
bannerUrl | string or null | |
bannerAlt | string | |
social | object | |
social.facebook | string or null | |
social.instagram | string or null | |
social.google | string or null | |
social.yelp | string or null | |
social.website | string or null | |
brandColor | string or null | The accent colour; null = SKUFlow blue. |
headerText | string or null | |
footerText | string or null | |
logoUrl | string or null | The business logo (Settings → General); change it with organizations.uploadLogo. |
businessName | string | The name the header and footer show (the sender name). |
address | string or null | The postal address in the footer (Settings → General). |
phone | string or null | The phone in the footer (Settings → Messaging). |
Change the email theme
PATCH /organizations/{organizationId}/messaging/email-theme
Changes how every email looks from the next one sent (automations, Send buttons, campaigns): only the fields sent change. A white logo on a white header is invisible: set headerBackground to a dark colour. Logo size: logoSize small | medium | large, or custom with logoWidth in px (kept within a phone's width). Preview first with POST /email-designs/preview and theme. Sends nothing.
| Permission | engagement.manage (Owner, Admin) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | emailDesigns_updateTheme (toolset marketing) |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
pageBackground | string | No | Around the email (#rrggbb). |
cardBackground | string | No | The email's body (#rrggbb). |
headerBackground | string | No | Behind the logo (#rrggbb): a dark one makes a white logo visible. |
textColor | string | No | |
linkColor | string | No | |
buttonColor | string or null | No | Buttons; null = the accent colour. |
buttonTextColor | string | No | Text on a filled button. |
buttonStyle | enum | No | One of filled, outline. |
buttonRadius | integer | No | Button corners, px. 0–24. |
fontFamily | enum | No | An email-safe font stack. One of system, arial, helvetica, verdana, tahoma, trebuchet, georgia, times, courier. |
logoSize | enum | No | small 32 px, medium 48 px (the default), large 72 px tall; custom = logoWidth. One of small, medium, large, custom. |
logoWidth | integer | No | The logo's width in px when logoSize is custom (never wider than the screen). 40–480. |
logoAlign | enum | No | One of left, center. |
showBrandBar | boolean | No | The accent-coloured bar across the top. |
bannerUrl | string or null | No | A banner image under the header: an uploaded image's url or https://; null removes it. Up to 1000 characters. |
bannerAlt | string | No | Up to 200 characters. |
social | object | No | Social links (https://), shown in the footer; null removes one. |
social.facebook | string or null | No | Up to 500 characters. |
social.instagram | string or null | No | Up to 500 characters. |
social.google | string or null | No | Up to 500 characters. |
social.yelp | string or null | No | Up to 500 characters. |
social.website | string or null | No | Up to 500 characters. |
brandColor | empty string or string or null | No | The accent colour (#rrggbb); empty or null = SKUFlow blue. |
headerText | string or null | No | A line under the logo. Up to 120 characters. |
footerText | string or null | No | Your footer text (website, hours, licence), above your address and unsubscribe. Up to 500 characters. |
Example
curl -X PATCH "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/messaging/email-theme" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"pageBackground":"string","cardBackground":"string","headerBackground":"string"}'Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
pageBackground | string | |
cardBackground | string | |
headerBackground | string | |
textColor | string | |
linkColor | string | |
buttonColor | string or null | null = the accent colour. |
buttonTextColor | string | |
buttonStyle | enum | One of filled, outline. |
buttonRadius | integer | |
fontFamily | string | |
logoSize | enum | One of small, medium, large, custom. |
logoWidth | integer | |
logoAlign | enum | One of left, center. |
showBrandBar | boolean | |
bannerUrl | string or null | |
bannerAlt | string | |
social | object | |
social.facebook | string or null | |
social.instagram | string or null | |
social.google | string or null | |
social.yelp | string or null | |
social.website | string or null | |
brandColor | string or null | The accent colour; null = SKUFlow blue. |
headerText | string or null | |
footerText | string or null | |
logoUrl | string or null | The business logo (Settings → General); change it with organizations.uploadLogo. |
businessName | string | The name the header and footer show (the sender name). |
address | string or null | The postal address in the footer (Settings → General). |
phone | string or null | The phone in the footer (Settings → Messaging). |
List the email designs
GET /organizations/{organizationId}/email-designs
Every email template (invoices, quotes, receipts, request confirmations and reminders, the Send buttons' emails, automations, marketing) and whether it has its own design or uses the default design (the theme and the template's text). Campaign designs are on each campaign (GET …/marketing/campaigns/{campaignId}/design). Read-only.
| Permission | engagement.read (Owner, Admin, Staff) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | emailDesigns_list (toolset marketing) |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/email-designs" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
An array of objects:
| Field | Type | Description |
|---|---|---|
templateId | string | |
name | string | |
subject | string or null | |
purpose | enum | One of TRANSACTIONAL, MARKETING. |
systemKey | string or null | |
isSystem | boolean | |
group | enum | transactional: invoices, quotes, receipts, request confirmations and the Send buttons; automation: other automations; marketing: win-back and other marketing. One of transactional, automation, marketing. |
recordKind | enum or null | One of invoice, quote, payment. |
custom | boolean | Has its own design (else the default design with the theme). |
updatedAt | string (date-time) or null |
Get an email template's design
GET /organizations/{organizationId}/message-templates/{templateId}/design
The template's block design (custom: true), or the starter it would begin from (custom: false: the default design plus its locked blocks), with the blocks it must keep (required: the message, a transactional email's summary such as the invoice's pay link, the footer). Read-only.
| Permission | engagement.read (Owner, Admin, Staff) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | emailDesigns_get (toolset marketing) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
templateId | path | string | Yes | An email message template id, from GET /email-designs (or GET /message-templates). |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/message-templates/{templateId}/design" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
target | object | |
target.kind | enum | One of template, campaign. |
target.id | string | |
target.name | string | |
target.subject | string or null | |
target.body | string | The message block's text (edit it on the template or campaign). |
target.preheader | string or null | |
target.purpose | enum | One of TRANSACTIONAL, MARKETING. |
target.systemKey | string or null | |
target.editable | boolean | False for a campaign that started (only drafts and scheduled ones change). |
custom | boolean | True: the email uses this design. False: the default design; document is the starter. |
document | object | |
document.version | integer | |
document.blocks | array of object | |
document.blocks[].id | string | |
document.blocks[].type | enum | One of header, heading, text, image, button, divider, spacer, columns, social, message, record, footer. |
required | array of object | Blocks this email must keep (they can move and change style, not be deleted). |
required[].type | enum | One of message, footer, record. |
required[].kind | enum | One of invoice, quote, payment. |
recordKind | enum or null | One of invoice, quote, payment. |
updatedAt | string (date-time) or null |
Save an email template's design
PUT /organizations/{organizationId}/message-templates/{templateId}/design
Replaces the template's design: every email it sends uses it from the next one (automations, the Send buttons, batch sends). 400 when a required block is missing (required from GET …/design) or a block is invalid. Sends nothing; check it first with POST /email-designs/preview or /email-designs/test.
| Permission | engagement.manage (Owner, Admin) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | emailDesigns_update (toolset marketing) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
templateId | path | string | Yes | An email message template id, from GET /email-designs (or GET /message-templates). |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
document | object | Yes | The design: { version: 1, blocks: [...] }. Blocks (each { id, type, style: { background, paddingTop, paddingBottom, paddingX } }): header { showHeaderText, showBanner }, heading { text, level 1-3, align }, text { text with **bold**, *italic*, label and {{merge.fields}} }, image { url (an uploaded image's url or https://), alt, href, width % , align }, button { label, url (https://… or a merge link such as {{links.invoice}}), align, fullWidth }, divider { color, thickness }, spacer { height }, columns { columns: 2-3 × { blocks: heading/text/image/button }, stackOnMobile }, social { align }, message (the template's or campaign's text), record { kind invoice|quote|visit|payment|membership, showButton, buttonLabel }, footer (your address and the unsubscribe link). GET the design first and send it back changed. |
document.version | 1 | No | Default 1. |
document.blocks | array of object | Yes | Up to 60 items. |
Example
curl -X PUT "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/message-templates/{templateId}/design" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"document":{"blocks":[{"id":"id_id","type":"header"}]}}'Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
target | object | |
target.kind | enum | One of template, campaign. |
target.id | string | |
target.name | string | |
target.subject | string or null | |
target.body | string | The message block's text (edit it on the template or campaign). |
target.preheader | string or null | |
target.purpose | enum | One of TRANSACTIONAL, MARKETING. |
target.systemKey | string or null | |
target.editable | boolean | False for a campaign that started (only drafts and scheduled ones change). |
custom | boolean | True: the email uses this design. False: the default design; document is the starter. |
document | object | |
document.version | integer | |
document.blocks | array of object | |
document.blocks[].id | string | |
document.blocks[].type | enum | One of header, heading, text, image, button, divider, spacer, columns, social, message, record, footer. |
required | array of object | Blocks this email must keep (they can move and change style, not be deleted). |
required[].type | enum | One of message, footer, record. |
required[].kind | enum | One of invoice, quote, payment. |
recordKind | enum or null | One of invoice, quote, payment. |
updatedAt | string (date-time) or null |
Reset an email template's design
DELETE /organizations/{organizationId}/message-templates/{templateId}/design
Destructive: deletes the template's own design; its emails go back to the default design (the theme and the template's text). The template itself is kept.
| Permission | engagement.manage (Owner, Admin) |
| Risk | sensitive, OAuth scope skuflow.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access. |
| MCP tool | emailDesigns_reset (toolset marketing) |
| Approval | Over MCP, this tool always waits for your approval in SKUFlow. See Approvals. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
templateId | path | string | Yes | An email message template id, from GET /email-designs (or GET /message-templates). |
Example
curl -X DELETE "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/message-templates/{templateId}/design" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
target | object | |
target.kind | enum | One of template, campaign. |
target.id | string | |
target.name | string | |
target.subject | string or null | |
target.body | string | The message block's text (edit it on the template or campaign). |
target.preheader | string or null | |
target.purpose | enum | One of TRANSACTIONAL, MARKETING. |
target.systemKey | string or null | |
target.editable | boolean | False for a campaign that started (only drafts and scheduled ones change). |
custom | boolean | True: the email uses this design. False: the default design; document is the starter. |
document | object | |
document.version | integer | |
document.blocks | array of object | |
document.blocks[].id | string | |
document.blocks[].type | enum | One of header, heading, text, image, button, divider, spacer, columns, social, message, record, footer. |
required | array of object | Blocks this email must keep (they can move and change style, not be deleted). |
required[].type | enum | One of message, footer, record. |
required[].kind | enum | One of invoice, quote, payment. |
recordKind | enum or null | One of invoice, quote, payment. |
updatedAt | string (date-time) or null |
Preview an email design
POST /organizations/{organizationId}/email-designs/preview
Renders an email with a design (unsaved document, else the saved one, else the default) and the theme (plus unsaved theme changes): the template's or campaign's text, merge fields filled with examples (or a real customerId's, no links made). Returns the HTML exactly as sent, the plain-text alternative and any locked block missing. Nothing is saved or sent.
| Permission | engagement.read (Owner, Admin, Staff) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | emailDesigns_preview (toolset marketing) |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
document | object | No | An unsaved design to preview; omit for the saved one (or the default). |
document.version | 1 | No | Default 1. |
document.blocks | array of object | Yes | Up to 60 items. |
templateId | string | No | Preview this email template's design and text. |
campaignId | string | No | Preview this campaign's design and text. |
theme | object | No | Theme changes not saved yet (the theme panel's live preview). |
theme.pageBackground | string | No | Around the email (#rrggbb). |
theme.cardBackground | string | No | The email's body (#rrggbb). |
theme.headerBackground | string | No | Behind the logo (#rrggbb): a dark one makes a white logo visible. |
theme.textColor | string | No | |
theme.linkColor | string | No | |
theme.buttonColor | string or null | No | Buttons; null = the accent colour. |
theme.buttonTextColor | string | No | Text on a filled button. |
theme.buttonStyle | enum | No | One of filled, outline. |
theme.buttonRadius | integer | No | Button corners, px. 0–24. |
theme.fontFamily | enum | No | An email-safe font stack. One of system, arial, helvetica, verdana, tahoma, trebuchet, georgia, times, courier. |
theme.logoSize | enum | No | small 32 px, medium 48 px (the default), large 72 px tall; custom = logoWidth. One of small, medium, large, custom. |
theme.logoWidth | integer | No | The logo's width in px when logoSize is custom (never wider than the screen). 40–480. |
theme.logoAlign | enum | No | One of left, center. |
theme.showBrandBar | boolean | No | The accent-coloured bar across the top. |
theme.bannerUrl | string or null | No | A banner image under the header: an uploaded image's url or https://; null removes it. Up to 1000 characters. |
theme.bannerAlt | string | No | Up to 200 characters. |
theme.social | object | No | Social links (https://), shown in the footer; null removes one. |
theme.social.facebook | string or null | No | Up to 500 characters. |
theme.social.instagram | string or null | No | Up to 500 characters. |
theme.social.google | string or null | No | Up to 500 characters. |
theme.social.yelp | string or null | No | Up to 500 characters. |
theme.social.website | string or null | No | Up to 500 characters. |
theme.brandColor | empty string or string or null | No | The accent colour (#rrggbb); empty or null = SKUFlow blue. |
theme.headerText | string or null | No | A line under the logo. Up to 120 characters. |
theme.footerText | string or null | No | Your footer text (website, hours, licence), above your address and unsubscribe. Up to 500 characters. |
customerId | string | No | Fill merge fields from this customer (GET /customers); examples when omitted. |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/email-designs/preview" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"document":{"blocks":[{"id":"id_id","type":"header"}]},"templateId":"template_id","campaignId":"campaign_id"}'Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
subject | string | |
preheader | string or null | |
html | string | The email's HTML, exactly as sent (merge fields filled). |
text | string | The plain-text alternative. |
unresolved | array of string | Merge fields with no value in this preview. |
problems | array of string | Why the design could not be saved as it is (a locked block missing). |
purpose | enum | One of TRANSACTIONAL, MARKETING. |
Send a test of an email design
POST /organizations/{organizationId}/email-designs/test
SENDS AN EMAIL to you (no customer): the design as previewed, merge fields filled with examples, subject marked "[Test]", to to (default your account's email) through the email provider (test mode only logs it). Logged in the message log with source TEST.
| Permission | engagement.manage (Owner, Admin) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | emailDesigns_sendTest (toolset marketing) |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
document | object | No | An unsaved design to preview; omit for the saved one (or the default). |
document.version | 1 | No | Default 1. |
document.blocks | array of object | Yes | Up to 60 items. |
templateId | string | No | Preview this email template's design and text. |
campaignId | string | No | Preview this campaign's design and text. |
theme | object | No | Theme changes not saved yet (the theme panel's live preview). |
theme.pageBackground | string | No | Around the email (#rrggbb). |
theme.cardBackground | string | No | The email's body (#rrggbb). |
theme.headerBackground | string | No | Behind the logo (#rrggbb): a dark one makes a white logo visible. |
theme.textColor | string | No | |
theme.linkColor | string | No | |
theme.buttonColor | string or null | No | Buttons; null = the accent colour. |
theme.buttonTextColor | string | No | Text on a filled button. |
theme.buttonStyle | enum | No | One of filled, outline. |
theme.buttonRadius | integer | No | Button corners, px. 0–24. |
theme.fontFamily | enum | No | An email-safe font stack. One of system, arial, helvetica, verdana, tahoma, trebuchet, georgia, times, courier. |
theme.logoSize | enum | No | small 32 px, medium 48 px (the default), large 72 px tall; custom = logoWidth. One of small, medium, large, custom. |
theme.logoWidth | integer | No | The logo's width in px when logoSize is custom (never wider than the screen). 40–480. |
theme.logoAlign | enum | No | One of left, center. |
theme.showBrandBar | boolean | No | The accent-coloured bar across the top. |
theme.bannerUrl | string or null | No | A banner image under the header: an uploaded image's url or https://; null removes it. Up to 1000 characters. |
theme.bannerAlt | string | No | Up to 200 characters. |
theme.social | object | No | Social links (https://), shown in the footer; null removes one. |
theme.social.facebook | string or null | No | Up to 500 characters. |
theme.social.instagram | string or null | No | Up to 500 characters. |
theme.social.google | string or null | No | Up to 500 characters. |
theme.social.yelp | string or null | No | Up to 500 characters. |
theme.social.website | string or null | No | Up to 500 characters. |
theme.brandColor | empty string or string or null | No | The accent colour (#rrggbb); empty or null = SKUFlow blue. |
theme.headerText | string or null | No | A line under the logo. Up to 120 characters. |
theme.footerText | string or null | No | Your footer text (website, hours, licence), above your address and unsubscribe. Up to 500 characters. |
to | string (email) | No | Where to send the test; default your account's email. Up to 254 characters. |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/email-designs/test" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"document":{"blocks":[{"id":"id_id","type":"header"}]},"templateId":"template_id","campaignId":"campaign_id"}'Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
status | string | SENT, SENT_TEST (test mode), SKIPPED or FAILED. |
statusReason | string or null | |
toAddress | string or null | |
subject | string or null |
Draft an email theme with AI
POST /organizations/{organizationId}/email-designs/ai/theme
Claude drafts the email theme's looks (accent, header and page backgrounds, text and link colours, buttons, font, logo alignment) from the business's brand: its website's theme, the logo's colours (logoColors, read from the logo by the caller) and its trade, plus an optional request. Text stays readable (contrast checked); no texts, logo or social links are changed. Uses AI credits (402 when none are left); source TEMPLATE when AI is off (a theme from the brand without AI). Nothing is saved: preview with POST /email-designs/preview theme, apply with PATCH /messaging/email-theme.
| Permission | engagement.manage (Owner, Admin) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | emailDesigns_draftTheme (toolset marketing) |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
prompt | string | No | Optional wishes: "darker header, rounded buttons, centred logo". Up to 600 characters. |
logoColors | array of string | No | The logo's main colours (#rrggbb, most used first), when known. Up to 5 items. |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/email-designs/ai/theme" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"string","logoColors":["string"]}'Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
theme | object | The drafted theme change (looks only: colours, buttons, font, logo alignment). Preview it with POST /email-designs/preview theme, apply it with PATCH /messaging/email-theme. |
theme.brandColor | string or null | |
theme.headerBackground | string | |
theme.pageBackground | string | |
theme.cardBackground | string | |
theme.textColor | string | |
theme.linkColor | string | |
theme.buttonColor | string or null | |
theme.buttonTextColor | string | |
theme.buttonStyle | enum | One of filled, outline. |
theme.buttonRadius | integer | |
theme.fontFamily | string | |
theme.logoAlign | enum | One of left, center. |
theme.showBrandBar | boolean | |
source | enum | Ai: Claude drafted it. TEMPLATE: AI is off or unavailable (a starter from your brand). One of AI, FAKE, TEMPLATE. |
basedOn | enum | Where the colours came from: the website's theme, the logo, the trade's style. One of website, logo, trade, default. |
notes | array of string | Why it looks this way (shown under the preview). |
Draft an email template's design with AI
POST /organizations/{organizationId}/message-templates/{templateId}/design/draft
Claude lays out the template's email as blocks from a short request ("bold, with a request-a-quote button"), using only the business's facts: images from its library, its links and merge fields; no prices or discounts that were not given. The message, a transactional email's summary (pay / approve link) and the footer are kept. Uses AI credits (402 when none are left); source TEMPLATE when AI is off (a starter layout). Nothing is saved: preview with POST /email-designs/preview document, save with PUT …/design. Read GET …/design first to see what must be kept.
| Permission | engagement.manage (Owner, Admin) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | emailDesigns_draftDesign (toolset marketing) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
templateId | path | string | Yes | An email message template id, from GET /email-designs (or GET /message-templates). |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
prompt | string | Yes | What the email should look like: "summer promo, bold, with a coupon button". Up to 600 characters. |
document | object | No | The design being edited with unsaved changes (the draft builds on it); omit for the saved one. |
document.version | 1 | No | Default 1. |
document.blocks | array of object | Yes | Up to 60 items. |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/message-templates/{templateId}/design/draft" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"string"}'Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
document | object | The drafted design ({ version: 1, blocks }), valid for this email and keeping its locked blocks and footer. Not saved: preview it with POST /email-designs/preview document, save it with PUT …/design. |
document.version | integer | |
document.blocks | array of object | |
document.blocks[].id | string | |
document.blocks[].type | enum | One of header, heading, text, image, button, divider, spacer, columns, social, message, record, footer. |
source | enum | Ai: Claude drafted it. TEMPLATE: AI is off or unavailable (a starter from your brand). One of AI, FAKE, TEMPLATE. |
notes | array of string | The AI's note on the design. |
warnings | array of string | What was left out of the AI's draft and why: an image not in the library, a link or price you didn't give, a merge field that doesn't exist. |
The image library for emails
GET /organizations/{organizationId}/email-designs/media
The business's images (its uploads and the stock photos it used on its website), most recently used first, paged by cursor: put an item's url in an image block or the theme's banner. Read-only.
| Permission | engagement.read (Owner, Admin, Staff) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | emailDesigns_media (toolset marketing) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
cursor | query | string | No | Up to 100 characters. |
limit | query | integer | No | 1–200. |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/email-designs/media" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
items | array of object | |
items[].id | string | |
items[].source | enum | One of UPLOAD, STOCK. |
items[].url | string | What to put in a section's image field. |
items[].thumbUrl | string or null | |
items[].alt | string | |
items[].width | integer or null | |
items[].height | integer or null | |
items[].provider | string or null | Stock: UNSPLASH or PEXELS. |
items[].providerId | string or null | |
items[].author | string or null | Stock: the photographer, credited on the site. |
items[].authorUrl | string or null | |
items[].sourceUrl | string or null | |
items[].createdAt | string (date-time) | |
items[].lastUsedAt | string (date-time) | |
nextCursor | string or null |
Upload an email image
POST /organizations/{organizationId}/email-designs/image-upload-url
Step 1 of adding an image to an email: a signed URL to PUT the file to (same Content-Type) and the url to put in an image block. Then POST /email-designs/media/uploads. AI callers use POST /email-designs/media/content instead.
| Permission | engagement.manage (Owner, Admin) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | emailDesigns_createImageUploadUrl (toolset marketing) |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
contentType | enum | Yes | One of image/jpeg, image/png, image/webp. |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/email-designs/image-upload-url" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"contentType":"image/jpeg"}'Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
signedUploadUrl | string (uri) | |
url | string |
Add an uploaded email image to the library
POST /organizations/{organizationId}/email-designs/media/uploads
Step 2 of uploading, after the PUT to the signed URL: the image joins the business's library. url must be one POST /email-designs/image-upload-url gave this business (404 otherwise).
| Permission | engagement.manage (Owner, Admin) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | emailDesigns_confirmImageUpload (toolset marketing) |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Up to 300 characters. |
width | integer or null | No | 1–20000. |
height | integer or null | No | 1–20000. |
alt | string or null | No | Up to 300 characters. |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/email-designs/media/uploads" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com"}'Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
source | enum | One of UPLOAD, STOCK. |
url | string | What to put in a section's image field. |
thumbUrl | string or null | |
alt | string | |
width | integer or null | |
height | integer or null | |
provider | string or null | Stock: UNSPLASH or PEXELS. |
providerId | string or null | |
author | string or null | Stock: the photographer, credited on the site. |
authorUrl | string or null | |
sourceUrl | string or null | |
createdAt | string (date-time) | |
lastUsedAt | string (date-time) |
Add an image for emails (content in the request)
POST /organizations/{organizationId}/email-designs/media/content
Adds a JPEG, PNG or WebP image to the business's library in one call: send contentBase64 or a public sourceUrl (up to 10 MB). Returns the library entry; put its url in an image block (PUT …/design) or the theme's bannerUrl.
| Permission | engagement.manage (Owner, Admin) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | emailDesigns_uploadImage (toolset marketing) |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
alt | string or null | No | Alternative text describing the image. Up to 300 characters. |
fileName | string | No | The file's name, e.g. receipt.jpg (default: from the URL, else 'file'). Up to 500 characters. |
contentType | string | No | MIME type, e.g. image/jpeg or application/pdf. Optional: taken from a data: URL, the URL's answer or the file's first bytes. Up to 200 characters. |
contentBase64 | string | No | The file's bytes as base64 (a data: URL is accepted too). Up to 10 MB. Give this or sourceUrl. |
sourceUrl | string (uri) | No | A public http(s) URL SKUFlow downloads the file from (up to 10 MB, 20 s, 3 redirects; private and internal addresses are refused). Give this or contentBase64. Up to 2000 characters. |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/email-designs/media/content" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"alt":"string","fileName":"string","contentType":"string"}'Response
201: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
source | enum | One of UPLOAD, STOCK. |
url | string | What to put in a section's image field. |
thumbUrl | string or null | |
alt | string | |
width | integer or null | |
height | integer or null | |
provider | string or null | Stock: UNSPLASH or PEXELS. |
providerId | string or null | |
author | string or null | Stock: the photographer, credited on the site. |
authorUrl | string or null | |
sourceUrl | string or null | |
createdAt | string (date-time) | |
lastUsedAt | string (date-time) |