REST endpoints for email designs in ServFlo: 14 operations with parameters, request and response fields, permissions and curl examples.
Base URL: https://app.servflo.ai/api. Every call needs Authorization: Bearer sfk_… (Authentication); {organizationId} ($ORGANIZATION_ID in the examples) is your business's id.
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.
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 servflo.write. Write: creates or changes records inside the business; nothing leaves it.
Every email template (invoices, quotes, receipts, booking 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.
transactional: invoices, quotes, receipts, bookings 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, visit, payment, membership.
custom
boolean
Has its own design (else the default design with the theme).
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.
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 servflo.write. Write: creates or changes records inside the business; nothing leaves it.
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 servflo.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 ServFlo. See Approvals.
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, Dispatcher)
Risk
write, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
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 servflo.write. Write: creates or changes records inside the business; nothing leaves it.
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 servflo.write. Write: creates or changes records inside the business; nothing leaves it.
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.
POST /organizations/{organizationId}/message-templates/{templateId}/design/draft
Claude lays out the template's email as blocks from a short request ("bold, with a booking 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 servflo.write. Write: creates or changes records inside the business; nothing leaves it.
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.
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.
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 servflo.write. Write: creates or changes records inside the business; nothing leaves it.
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 servflo.write. Write: creates or changes records inside the business; nothing leaves it.
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 servflo.write. Write: creates or changes records inside the business; nothing leaves it.
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 ServFlo 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.