Emails API
Endpoints for email templates, email settings, the sender domain, the setup checklist, the email log and suppressions, with curl examples and MCP tools.
Emails API
The Emails API manages the emails Flo sends your customers: their templates, the switch and settings of each email type, the Reply-To and sender domain, the setup checklist, the email log and the suppression list. The Emails guide explains how these parts fit together.
Every endpoint acts on the authenticated client only. Authenticate with client Basic authentication, using your client identifier (client.uuid) as the username and an active API token as the password, or with an OAuth access token (Authorization: Bearer). In the examples, FLOPAY_BASIC_AUTH holds the Base64 encoding of clientUuid:apiToken. A missing or invalid credential answers 401.
Each endpoint is also a FloPay MCP tool, named under it, except confirming the email review. See Use it from an MCP assistant.
Templates
A template is your own content for one email, keyed by its message type, for example customer.order_confirmation. The {type} in these paths is that message type, as GET /v1/email-templates lists it. An unknown type answers 404. How drafts, previews, test sends and publishing fit together is in Templates in the guide.
List templates
GET /v1/email-templates
Lists every email you can give your own template. The list is paged with page and limit (at most 100, 20 by default), sorts by type, and filters by emailType, enabled (true or false) and source (default or published).
curl "https://api.flopay.com/v1/email-templates?enabled=true" \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH"{
"data": [
{
"type": "customer.order_confirmation",
"emailType": "order_confirmation",
"enabled": true,
"source": "published",
"publishedVersion": 2,
"publishedAt": "2026-11-03T09:12:00.000Z",
"hasDraft": false
}
],
"page": 1,
"limit": 20,
"pages": 1,
"total": 1
}| Field | Meaning |
|---|---|
type | The message type, used as {type} in the other template paths. |
emailType | The email type whose switch controls this email in Settings, or null when no switch controls it. |
enabled | Whether the email is sent. An email no switch controls is always sent. |
source | What is sent: default for Flo's default, published for your published template. |
publishedVersion, publishedAt | The template that is sent and when it was published, or null while Flo's default is sent. |
hasDraft | Whether there is an unpublished draft. |
MCP tool: EmailTemplate_list.
List variable schemas
GET /v1/email-templates/schemas
Returns the variable schema of every email type: the variables a template may use, with their types, descriptions, nullability and sample values, the links every template of the type must contain, and a preview sample. GET /v1/email-templates/schemas/{type} returns the schema of one type, for example customer.order_confirmation. The Variables and filters section of the guide explains how to use them.
curl https://api.flopay.com/v1/email-templates/schemas \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH"curl https://api.flopay.com/v1/email-templates/schemas/customer.order_confirmation \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH"Each schema has its type, its version, its variables by group (client, customer, items, payment, subscription, checkout_session and links, where items is a list) and a sample for the whole email. Adding a variable keeps the version; removing a deprecated variable raises it by one.
MCP tools: EmailTemplateSchema_list and EmailTemplateSchema_get.
Get a template
GET /v1/email-templates/{type}
Returns your template for one email. Reading a type you never edited stores nothing.
curl https://api.flopay.com/v1/email-templates/customer.order_confirmation \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH"| Field | Meaning |
|---|---|
type | The message type. |
source | What is sent: published for your published template, default for Flo's default. |
publishedVersion | The number of the published template that is sent, or null while Flo's default is sent. |
published | The published template that is sent (mode, its content in body, publishedAt and publishedBy), or null. |
draft | The unpublished draft, or null when there is none. |
defaultBody | Flo's current default for the email, in structured slots. |
lastReset | When (at) and by whom (by) the template was last reset to the default, or null. |
MCP tool: EmailTemplate_get.
Save a draft
PUT /v1/email-templates/{type}/draft
Saves the draft, in structured slots or custom HTML. Nothing that is sent changes until the draft is published. The answer is the template, as Get a template returns it.
For structured slots, send mode set to structured and the slots in content. subject, heading and paragraphs are required. preheader, brandedSubject, highlight, button, buttons, closing and variableDefaults are optional. A text slot is one wording or a list of wordings, each as { "text": "...", "requires": [...] }: the first wording whose variables all have a value is used.
curl -X PUT https://api.flopay.com/v1/email-templates/customer.order_confirmation/draft \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH" \
-H "Content-Type: application/json" \
-d '{
"mode": "structured",
"content": {
"subject": "Your order from {{ client.name }}",
"heading": "Thanks for your order",
"paragraphs": [
"We received {{ payment.amount | money }}.",
"Your payment reference is {{ payment.reference }}."
]
}
}'For custom HTML, send mode set to html, the subject and the html body.
curl -X PUT https://api.flopay.com/v1/email-templates/customer.order_confirmation/draft \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH" \
-H "Content-Type: application/json" \
-d '{
"mode": "html",
"subject": "Your order from {{ client.name }}",
"html": "<h1>Thanks for your order</h1><p>We received {{ payment.amount | money }}.</p>"
}'Each text is at most 100 KB. A body with an unknown mode, no subject, or a text over 100 KB answers 400.
MCP tool: EmailTemplate_saveDraft.
Preview a template
POST /v1/email-templates/{type}/preview
Renders the draft, or the content currently sent when there is no draft, the way a real email is rendered, with the sample data of the type and your brand. Send source set to sample. A preview changes nothing.
curl -X POST https://api.flopay.com/v1/email-templates/customer.order_confirmation/preview \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH" \
-H "Content-Type: application/json" \
-d '{ "source": "sample" }'The answer has the rendered subject, html and text, a status and the problems that publishing the same content would refuse. Problems come back in the body, never as an error: status is rendered when the content renders and invalid when it has problems. Each problem has a code (liquid_parse_error, unknown_variable, missing_required_link, render_error or invalid_template), a readable message, and the path, line and column it is about when it has them.
MCP tool: EmailTemplate_preview.
Send a test email
POST /v1/email-templates/{type}/test-send
Sends the draft, or the content currently sent when there is no draft, to one address in to, rendered with the sample data. The subject starts with [Test]. The template and the switches do not change.
curl -X POST https://api.flopay.com/v1/email-templates/customer.order_confirmation/test-send \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH" \
-H "Content-Type: application/json" \
-d '{ "to": "owner@shop.com" }'A test email that is sent answers 202:
{
"status": "sent",
"reason": null,
"retryAfterSeconds": null
}Test sends are limited to 10 per rolling hour and 50 per rolling day for your account.
| Status | When |
|---|---|
400 | to is missing or is not exactly one valid address. |
422 | The content cannot be rendered (not_renderable), or the address is on your suppression list (suppressed). |
429 | The test-send limit is reached. Retry-After and retryAfterSeconds say when to try again. |
503 | Email delivery is not enabled, so no test email can be sent. |
MCP tool: EmailTemplate_testSend.
Publish a draft
POST /v1/email-templates/{type}/publish
Validates the draft and publishes it: from then on it is what your customers receive. The answer is the published template, with its mode, its content in body, publishedAt and publishedBy (the user for OAuth calls, the API token for Basic).
curl -X POST https://api.flopay.com/v1/email-templates/customer.order_confirmation/publish \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH"| Status | When |
|---|---|
409 | There is no draft to publish. |
422 | The draft fails validation. reasons lists every problem, and nothing is published. |
MCP tool: EmailTemplate_publish.
Reset to the default
POST /v1/email-templates/{type}/reset
Discards the draft and stops sending your published template, so customers receive Flo's default again. Who reset it is recorded in lastReset. The answer is the template, as Get a template returns it.
curl -X POST https://api.flopay.com/v1/email-templates/customer.order_confirmation/reset \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH"MCP tool: EmailTemplate_reset.
Settings
Email settings hold your Reply-To and, for every email type, whether it is sent and its settings. The types and their defaults are explained in Switches and defaults in the guide.
Read email settings
GET /v1/emails/settings
curl https://api.flopay.com/v1/emails/settings \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH"{
"replyTo": "support@shop.com",
"types": [
{
"type": "dunning_first_failure",
"enabled": true,
"enabledAt": "2026-11-02T10:00:00.000Z",
"config": {},
"declarations": []
},
{
"type": "dunning_final",
"enabled": false,
"enabledAt": null,
"config": {},
"declarations": []
}
]
}| Field | Meaning |
|---|---|
replyTo | Your one Reply-To, the same value as replyTo of the sender settings, or null until you set one. |
types[].type | The email type, for example order_confirmation. |
types[].enabled | Whether the email is sent. A type you never chose is off. |
types[].enabledAt | When it was last switched on, or null. |
types[].config | The settings of the type. |
types[].declarations | The settings the type accepts, each with its key, label, kind (integer, boolean, string or integer_list), default, min, max and help. Empty for a type with no settings. |
MCP tool: EmailSettings_get.
Switch an email on or off
PATCH /v1/emails/settings/{type}
Switches one email type on or off with enabled, and saves its settings in config. Send either or both. The {type} is the email type, as GET /v1/emails/settings lists it. The answer is that type.
curl -X PATCH https://api.flopay.com/v1/emails/settings/dunning_final \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH" \
-H "Content-Type: application/json" \
-d '{ "enabled": true }'A config key the type does not declare, a value of the wrong kind, or a value outside the declared min and max answers 400 naming the field, and nothing is stored. Keys you leave out take the type's defaults. An unknown type answers 404.
MCP tool: EmailSettings_updateType.
Set the Reply-To
PATCH /v1/emails/settings
Sets your one Reply-To: one email address of at most 254 characters. It is the same value as replyTo of the sender settings, so setting it here changes it there. The answer is the same as Read email settings.
curl -X PATCH https://api.flopay.com/v1/emails/settings \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH" \
-H "Content-Type: application/json" \
-d '{ "replyTo": "support@shop.com" }'A stored Reply-To cannot be cleared: null or a blank value answers 400 while one is stored. A list, a display name or anything else that is not one address answers 400, and nothing is stored.
MCP tool: EmailSettings_updateReplyTo.
Sender domain
The sender settings and the delegated sender domain. By default your emails go from no-reply@flopay.com as your brand name "via Flopay", with your Reply-To. A delegated subdomain is the advanced option. How it works, and what the dashboard shows, is in Sender domain in the guide.
Read sender settings
GET /v1/emails/sender/settings
Returns your Reply-To and the From local part used once your sender domain is verified. Before you save settings, replyTo is null and fromLocalPart is billing.
curl https://api.flopay.com/v1/emails/sender/settings \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH"{
"replyTo": "support@shop.com",
"fromLocalPart": "billing"
}MCP tool: Sender_getSettings.
Update sender settings
PUT /v1/emails/sender/settings
Sets the Reply-To and the From local part. A field you leave out keeps its value.
replyTo: one email address of at most 254 characters. It is required for a complete setup and cannot be cleared.fromLocalPart: 1 to 64 lower-case letters, digits,.,_or-. With the verified sender domainmail.shop.com,billingsends frombilling@mail.shop.com.
curl -X PUT https://api.flopay.com/v1/emails/sender/settings \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH" \
-H "Content-Type: application/json" \
-d '{ "replyTo": "support@shop.com", "fromLocalPart": "billing" }'An invalid value answers 400 with a message naming the field.
MCP tool: Sender_updateSettings.
Add a sender domain
POST /v1/emails/sender/domain
Starts the delegation of one subdomain of your domain (without a scheme or path), with the label mail unless you send another label. Flo hosts the subdomain and writes the records mail needs inside it. The answer is pending, with exactly the four NS records you publish at your DNS host.
curl -X POST https://api.flopay.com/v1/emails/sender/domain \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH" \
-H "Content-Type: application/json" \
-d '{ "domain": "shop.com" }'{
"name": "mail.shop.com",
"status": "pending",
"records": [
{ "type": "NS", "name": "mail.shop.com", "value": "ns1.dns.flopay.com" },
{ "type": "NS", "name": "mail.shop.com", "value": "ns2.dns.flopay.com" },
{ "type": "NS", "name": "mail.shop.com", "value": "ns3.dns.flopay.com" },
{ "type": "NS", "name": "mail.shop.com", "value": "ns4.dns.flopay.com" }
],
"verifiedAt": null,
"brokenReason": null
}These four NS records at mail.<client-domain> are the only records you publish. Nothing changes at your root domain.
| Status | When |
|---|---|
400 | The domain or label is malformed, or is a Flopay domain. |
409 | You already have a sender domain, or the subdomain is in use. |
MCP tool: Sender_addDomain.
Read the sender domain
GET /v1/emails/sender/domain
Returns your sender domain in the same shape as Add a sender domain, with its current status: pending (the delegation is started and not verified yet), verified (mail goes from the domain) or broken (it was verified and now fails, with the reason in brokenReason). Without a sender domain it answers 404.
curl https://api.flopay.com/v1/emails/sender/domain \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH"MCP tool: Sender_getDomain.
Verify the sender domain
POST /v1/emails/sender/domain/verify
Checks each NS record of the delegation and the records Flo wrote in the subdomain. When every check passes, the domain is verified and new mail goes from it. Otherwise failing names the checks that failed. A DNS lookup that times out reads unknown and changes nothing. Without a sender domain it answers 404.
curl -X POST https://api.flopay.com/v1/emails/sender/domain/verify \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH"{
"status": "verified",
"nsRecords": [
{ "name": "mail.shop.com", "value": "ns1.dns.flopay.com", "check": "found" },
{ "name": "mail.shop.com", "value": "ns2.dns.flopay.com", "check": "found" },
{ "name": "mail.shop.com", "value": "ns3.dns.flopay.com", "check": "found" },
{ "name": "mail.shop.com", "value": "ns4.dns.flopay.com", "check": "found" }
],
"dkim": "found",
"returnPath": "found",
"failing": [],
"verifiedAt": "2026-11-04T10:00:00.000Z"
}Each check reads found, missing or unknown. nsRecords has one entry for each of the four NS records you publish. dkim and returnPath check the two records Flo writes in the subdomain itself: you never add them.
MCP tool: Sender_verifyDomain.
Remove the sender domain
DELETE /v1/emails/sender/domain
Removes the sender domain. Mail goes from no-reply@flopay.com again at once. Flo keeps the empty subdomain until you take the four NS records off your DNS host. Answers 204, or 404 without a sender domain.
curl -X DELETE https://api.flopay.com/v1/emails/sender/domain \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH"MCP tool: Sender_removeDomain.
Setup checklist
The setup steps of your account, in the order to show them. Render the list as data: a step added later appears without a dashboard release, so do not depend on its keys or length.
Read the checklist
GET /v1/setup-checklist
curl https://api.flopay.com/v1/setup-checklist \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH"{
"complete": false,
"steps": [
{ "key": "brand_set", "title": "Set up your brand", "done": true, "optional": false },
{ "key": "gateway_connected", "title": "Connect a gateway", "done": true, "optional": false },
{ "key": "reply_to_set", "title": "Set your Reply-To", "done": true, "optional": false },
{ "key": "sender_domain_verified", "title": "Verify your sender domain", "done": false, "optional": true },
{ "key": "emails_reviewed", "title": "Review your emails", "done": false, "optional": false }
]
}| Field | Meaning |
|---|---|
complete | true while every step that is not optional is done. |
steps[].key | The step, stable across releases: brand_set, gateway_connected, reply_to_set, sender_domain_verified or emails_reviewed. |
steps[].title | What to do, ready to show. The titles in the example are illustrative: show the text the API returns. |
steps[].done | Whether the step is done. Brand, gateway, Reply-To and sender domain follow their current state on every read, so a sender domain that breaks after verification reads not done again. |
steps[].optional | Whether the step is optional. Only sender_domain_verified is optional: it reports its real state but never holds back complete. |
MCP tool: SetupChecklist_get.
Confirm the email review
POST /v1/setup-checklist/review
Records that you reviewed your emails, with when and by which user, and answers the checklist. Confirming again succeeds and keeps the first confirmation. Changing switches, settings or the Reply-To never counts as the review.
curl -X POST https://api.flopay.com/v1/setup-checklist/review \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH"Confirming the review is your own act, so it is REST only and has no MCP tool.
Email log
GET /v1/emails/log
Every email Flo sent or tried to send for you, newest first. The list is paged with page and limit (at most 100), sorts by createdAt or updatedAt, and filters by status, type, recipient, linkedResourceType, linkedResourceId and createdAt (with [gte], [gt], [lte] or [lt] for a range). status, type, recipient, linkedResourceType and linkedResourceId also accept [in] with a comma-separated list. The email log is read-only. What each status means is in Email log and suppression in the guide.
Every email about one payment:
curl "https://api.flopay.com/v1/emails/log?linkedResourceType=payment&linkedResourceId=7c9e6679-7425-40de-944b-e07fc1f90ae7" \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH"Every email that bounced or drew a complaint:
curl "https://api.flopay.com/v1/emails/log?status[in]=bounced,complained" \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH"{
"data": [
{
"id": "2f1d0c4e-8b7a-4d3c-9e6f-5a4b3c2d1e0f",
"clientId": "4f1c2b7e-9a3d-4e8f-b2c6-1d5a7e9f0c3b",
"outboxMessageId": "b8e2a6d4-1c3f-4e5a-9b7d-0f2e4c6a8b1d",
"messageType": "customer.order_confirmation",
"recipientEmail": "ada@example.com",
"status": "delivered",
"skipReason": null,
"bounceType": null,
"providerMessageId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"linkedResourceType": "payment",
"linkedResourceId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"sentAt": "2026-11-02T10:00:04.000Z",
"createdAt": "2026-11-02T10:00:02.000Z",
"updatedAt": "2026-11-02T10:00:09.000Z"
}
],
"page": 1,
"limit": 20,
"pages": 1,
"total": 1
}| Field | Meaning |
|---|---|
status | The delivery status: queued, sent, delivered, bounced, complained, suppressed or skipped. |
skipReason | Why the email ended as skipped, otherwise null. |
bounceType | The bounce type once the email bounced, otherwise null. |
linkedResourceType, linkedResourceId | The payment, subscription or checkout_session the email is about, when known. |
sentAt | When the email provider accepted the email, otherwise null. |
outboxMessageId | One entry per message, across retries. |
An invalid query answers 400.
MCP tool: EmailLog_listLog.
Suppression list
GET /v1/emails/suppressions
The addresses Flo no longer sends to for you, after a hard bounce or a spam complaint. The list is paged with page and limit, sorts by createdAt, and filters by recipient, reason (hard_bounce or complaint) and createdAt. The suppression list is read-only: no endpoint adds or removes an address.
curl "https://api.flopay.com/v1/emails/suppressions?reason=hard_bounce" \
-H "Authorization: Basic $FLOPAY_BASIC_AUTH"{
"data": [
{
"id": "9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a",
"clientId": "4f1c2b7e-9a3d-4e8f-b2c6-1d5a7e9f0c3b",
"email": "old-address@example.com",
"reason": "hard_bounce",
"sourceEntryId": "3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7",
"createdAt": "2026-11-03T08:30:00.000Z"
}
],
"page": 1,
"limit": 20,
"pages": 1,
"total": 1
}sourceEntryId is the email log entry whose bounce or complaint suppressed the address, when known. An invalid query answers 400.
MCP tool: EmailLog_listSuppressions.
Use it from an MCP assistant
Every endpoint on this page except POST /v1/setup-checklist/review is a FloPay MCP tool, named under the endpoint. Reads are read-only tools for owners, admins and members. Changes are actions for owners and admins: the assistant shows you a preview and runs the action only after you confirm it. Adding, verifying and removing a sender domain and changing the sender settings are high-risk actions. The tool reference lists the arguments, results and policy of every tool.
Related
- Emails guide: email types, templates, variables, switches, the sender domain and the email log.
- Variables and filters
- Sender domain
- API tokens
Statement descriptors
The statementDescriptor field on products, collections, and your company, with its validation rules, the cascade that picks one value per charge, the warnings returned when you save, the gateway prefix fields, and the descriptor recorded on each payment.
Customer merge
Merge a duplicate customer into the one you keep with the FloPay API, covering the request, preview, idempotency, errors, and the MCP action.