Flodesk
Analyze your email audience
- Category
- Marketing
- Primary Subcategory
- Email & SMS Lifecycle Marketing Automation
Integration details
Description
Flodesk helps users analyze email, subscriber, segment, workflow, form, and checkout performance; inspect and manage subscriber profiles and segment membership; and create reusable audience segments through ChatGPT.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Primary Subcategory
- Email & SMS Lifecycle Marketing Automation
- Secondary Subcategories
- None listed
- Brand
- Flodesk
- Access
- Account required
- First tracked
- 2026-08-13
- Tool count
- 34
- Geography
- US
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
Get alerts for Flodesk
Get updates when Flodesk’s Discoverability Score or category rank changes.
ChatGPT Plugin Discovery Score
ChatGPT Plugin discovery is coming soon
ChatGPT can surface a Plugin when it matches a user's request.Your Plugin Discovery Score measures how often yours appears.
No spam. Unsubscribe any time.
What discovery looks like

Competing in ChatGPT Email & SMS Lifecycle Marketing Automation
View Category34 tools agents can invoke
Returns the authenticated Flodesk user's account profile: their id, email, name, timezone, and country. Use for "who am I logged in as?" or to personalize a response with the user's name. Takes no inputs. Returns a JSON object as text. For email-campaign performance, use `get_email_totals`. For subscriber-list size, use `get_subscriber_totals`. To look up someone else's profile, no tool exists today.
get_me
Adds one existing Flodesk subscriber, identified by exactly one of `email` or `id`, to one existing static segment identified by `segment_id`. This is an additive membership write: it never creates a subscriber, never creates a segment, and never removes or overwrites anything. Use to put ONE named subscriber into a group — "add [email protected] to my VIP segment". To add everyone matching a filter instead of a named person, use `bulk_add_subscribers_to_segment`. Resolve a segment name to its `segment_id` with `list_segments` first; this tool takes only the id, and only static segments accept manual membership (dynamic and pre-built segments are rule-computed and are rejected with an error). To create a brand-new subscriber use `create_subscriber`. Pass exactly one of `email` or `id` (supplying both or neither returns an error), plus `segment_id`. Returns a JSON object as text with `added` (true when the membership was newly created, false when the subscriber was already in the segment — both are success), `subscriber` (id, email), `segment` (id, name), and a short `message` summarizing what happened.
add_subscriber_to_segment
Answers "what automations/workflows do I have running", "best performing workflow", "workflow open/click rates", "rank my automations", "which sequences convert best", "where do subscribers drop off in workflow X". Returns the user's automation workflows ranked by a metric — each with its name, status, how many subscribers entered and completed it, the completion rate, the number of active subscribers still in it, and its open, click, and unsubscribe rates. The ranked list returns 10 workflows per page by default, up to 20 via `per_page`; pass `page` (1-based) to fetch more. Pass `workflow_id` to get one workflow's structure (its steps/nodes, emails, and edges) with per-node analytics: each node carries the number of subscribers currently at that step, the total times it was entered, and the total times it was completed; email-send nodes additionally carry unique opens and clicks. These per-node values are integer counts, not rates — derive step conversion (completed ÷ entered) and drop-off between adjacent steps yourself. The click counts here are totals per email and never name the link urls behind them, so this tool cannot answer which links or urls were clicked in a workflow — for that use `list_workflow_links`. The ranked-list rates (completion, open, click, unsubscribe) are decimals between 0 and 1 (e.g., 0.27 = 27%). Lifetime by default; pass `from`/`to` (ISO date `2026-03-01` or RFC3339) to scope the ranked list's entries, completions, and engagement to that window (`to` defaults to today, max 365-day span). The single-`workflow_id` detail view is always lifetime and does not accept `from`/`to`. Use for **listing and comparing automated email sequences** and **finding where a workflow loses subscribers**. Note: does not return revenue attribution — that requires the Workflow API, not Analytics.
rank_workflows
Answers "what's the best day to send", "best time of day for opens", "when do my subscribers engage most", "optimal send time", "should I send mornings or evenings". Returns campaign engagement aggregated by one dimension: day-of-week, time-of-day, or device-type. Day-of-week returns open and click rates; time-of-day and device-type return unique-open/click counts plus a share-of-total percentage. All rates and percentages are decimals between 0 and 1 (e.g., 0.27 = 27%). Pass `period` (last7Days, last30Days, or last90Days) — or, alternatively, a `from`/`to` window (ISO date `2026-03-01` or RFC3339; `to` defaults to today, max 365-day span) — to scope to a range. Pass `period` or `from`/`to`, not both. Use for **timing and scheduling decisions**.
get_send_time_insights
Returns the Flodesk account's brand settings: business name, website, logo image URL, mailing address, brand color palette, uploaded brand font families, and the social profile links the account has set. Use for "what are my brand colors", "what's my brand", "what's my website", "where can people follow me", or to ground copy and visuals in the account's own branding. Takes no inputs. Returns a JSON object as text; fields the account has not set are omitted, and an account that has configured nothing returns a short explanatory message instead. Colors are keyed by positional palette slot (`color1`, `color2`, and so on) with hex values; the slot number carries no meaning, so there is no primary or accent slot to read into it. Social links are keyed by network name, where `twitter` is the key for X. Font families list only fonts uploaded to this account — an absent list means no custom uploads, not that the brand has no typography. For the login profile (email, timezone, account country code), use `get_me`.
get_brand_settings
Adds every subscriber matching a filter to one existing static segment, in a single background job. This is an additive membership write: it never creates subscribers, never creates the segment, and never removes anyone from it. Use for cohort-scale grouping — "put everyone who bought in the last 90 days into my Buyers segment", "add everyone who joined through my welcome form to Newsletter". To add ONE known subscriber use `add_subscriber_to_segment` instead; to see how many people match and who they are before anything changes, and to obtain the `confirmation_token` this tool requires, use `preview_bulk_change` with `action: "add_to_segment"`; for a bare count with no change in prospect use `preview_segment_count`. `bulk_remove_subscribers_from_segment` is the inverse operation but not a one-step undo: its filter has to carry a `segmentIds` condition for this segment, which this one does not, and it removes everyone the filter matches — including people who were in the segment before this call. Requires a `confirmation_token` from `preview_bulk_change` issued for `action: "add_to_segment"` — pass the same `filter`, `segment_id` and `signup_form` here, unchanged, because the token is bound to the exact group AND the exact destination segment that were previewed. A rebuilt filter and a different destination are rejected separately, so the refusal says which one to correct. Each token works once: a second call with the same one is refused and queues nothing. The cohort is counted again before anything is queued: if it has grown substantially since the preview, the change is refused rather than applied to people who were not in the number shown. The count from that fresh check is what is returned as `subscribers_matched` — what was queued, not a guarantee of how many memberships changed, since anyone already in the segment is skipped — with `previewed_before` alongside it whenever that differs from the count the token was issued for. If nobody matches any more, nothing is queued and the result says so, with `subscribers_matched` of 0 — the token is spent either way. Only static segments accept manual membership changes; dynamic and pre-built segments are rule-computed and are rejected with an error. Resolve a segment name to its `segment_id` with `list_segments` first; this tool takes only the id. The work runs in the background and is NOT finished when this returns: `status` is `processing` when it begins immediately, or `waiting` when another bulk operation is already running. Subscribers already in the target state are skipped without the job failing, so a partial result is normal rather than an error. Returns a JSON object as text: `status`, `subscribers_matched` (how many matched when the work was queued — not a count of what has changed, since nothing has completed yet), a plain-language `message` describing what is happening, and the `filter` and `segment_id` echoed back so the same selection can be reused or reversed, plus any `signup_form` that was given.
bulk_add_subscribers_to_segment
Archives every subscriber matching a filter, which stops sending to them without deleting them or their data, and takes them out of the active subscriber count for the account. Reversible with `bulk_unarchive_subscribers`. Archiving is rate limited per subscriber to once every 45 days, counted from when that subscriber was last archived; the 45-day window on unarchiving is tracked separately and does not block an archive. Requires a `confirmation_token` from `preview_bulk_change` — pass the same `filter` and `signup_form` here, unchanged, because the token is bound to the exact selector that was previewed and a rebuilt one is rejected. The work runs in the background and is NOT finished when this returns: `status` is `processing` when it begins immediately, `waiting` when another bulk operation is already running, or `nothing_to_do` when the group emptied between the preview and now, in which case nothing was queued and the token is spent. Individual subscribers can be skipped without the job failing, either because they are not eligible for the change — already in the target state, or in some other status the change does not apply to — or because that subscriber is inside its own rate-limit window, so a partial result is normal rather than a failure. The cohort is counted again before anything is queued: if it has grown substantially since the preview, the change is refused rather than applied to people who were not in the number shown. Returns a JSON object as text: `status`, `subscribers_matched` (how many matched when the work was queued — not a count of what has changed, since nothing has completed yet), `previewed_before` when that differs from the count the token was issued for, so a group that moved between the preview and the write is visible rather than silently replaced, a plain-language `message` describing what is happening, and the `filter` plus any `signup_form` echoed back so the same selector can be reused.
bulk_archive_subscribers
Returns how many subscribers match the group a cohort-scale operation would act on, a sample of who they are, and a single-use token that pins that group for the tool which follows. This tool changes no subscriber and queues no work. It is the dry-run step in front of a bulk archive, unarchive, segment addition, segment removal, or CSV export: "how many people would this affect", "show me who matches before anything happens". Archiving stops sending to someone without deleting them, and takes them out of the account's active subscriber count; adding to a segment can start a workflow that sends email; an export alters no subscriber at all and produces a file. `action` names the operation being previewed (archive, unarchive, add_to_segment, remove_from_segment, or export); the token is valid only for that action and that exact selection, and the matching tool will not run without it. `filter` names the group for every action except export, which takes either `filter` or `segment_id` and never both. `segment_id` means something different per action and is rejected where it does not apply: for add_to_segment it is the segment those subscribers would be added to, and the token is bound to it, so spending that token on any other segment is refused; for export it names a saved segment to export instead of an ad-hoc filter, static or rule-based either way; archive, unarchive and remove_from_segment take no `segment_id` at all. For remove_from_segment the filter needs a `segmentIds` condition naming the segment in question, joined with `match: "all"`, so `affected` counts the people actually in it rather than everyone matching the other conditions — that exact shape is what the write accepts, and the multi-segment `match: "any"` pattern below does not apply to it. The token records what was previewed — it is not a substitute for the confirmation the user gives when the write tool runs. Pass `filter.conditions` as an array of 1–10 `{ field, operator, value }` objects, even for one condition; `match` joins them ('all' = AND, the default; 'any' = OR). Fields: date `createdAt`/`lastActiveAt`/`lastUnsubscribedAt` (absolute RFC-3339, or relative like "-90 days"); rate `openRate`/`clickRate` as decimals between 0 and 1 (e.g., 0.27 = 27%); engagement counts `openedEmails`/`clickedEmails`/`deliveredEmails` over a window (`_l7d`/`_l30d`/`_l90d`/`_l180d`/`_l365d`) or lifetime (no suffix); `status` (active, unconfirmed, unsubscribed, bounced, complained, cleaned, archived); and `sourceType` (signup form, manual, import, integration, checkout, product catalog — checkout and product catalog are separate values that both cover buyers, so include both with `match: "any"` to reach every buyer); and `segmentIds`, which matches subscribers already in a segment and takes ONE segment id (from `list_segments`) per condition — repeat the condition with `match: "any"` to cover several segments. Operators: `gt`/`ge`/`lt`/`le` for dates, rates, and counts (use `ge`/`le`, not `gte`/`lte`); counts also allow `eq`; `status`/`sourceType` use `eq`/`ne` only; `segmentIds` uses `contains` only. An empty `conditions` array is rejected. `signup_form` takes a form name (not an id) and restricts the cohort to subscribers who joined through that form. This is the tool that answers how many AND who, ahead of an operation. Before any specific operation has been decided, a size alone is `preview_segment_count`, which returns a number and no subscriber rows, and browsing more of the matching people is `list_subscribers`; to save the filter as a segment use `create_segment`. Returns a JSON object as text: `affected` (the true number of matching subscribers, independent of the sample size), `sample` (up to 10 of them, newest subscribers first (by when they joined the account), each with id, email, and status), `action` echoed back, the selector echoed back as `filter` or as `segment_id`, `signup_form` when one was given, and — when `affected` is above zero — `confirmation_token` with `expires_in_seconds`. A group of zero returns no token and a `message` saying so, because there is nothing to act on.
preview_bulk_change
Removes every subscriber matching a filter from one existing static segment, in a single background job. The subscribers themselves are not deleted, unsubscribed, or changed in any other way — only their membership of that segment ends. Use for cohort-scale pruning — "take everyone inactive for a year out of my VIP segment". To remove ONE known subscriber use `remove_subscriber_from_segment` instead. `bulk_add_subscribers_to_segment` is the inverse operation, though this filter cannot undo the change: once these members are gone, the `segmentIds` condition below matches nobody, so putting them back means selecting them by some other attribute. The `filter` must use `match: "all"` and include a `segmentIds` condition whose value is the same `segment_id`, with no segmentIds condition naming a different segment. Anything else is rejected: without the condition, or joined with `match: "any"`, the filter also selects people outside the segment, the previewed number describes a larger group than the one being changed, and every member of the segment is removed rather than the group that was shown. To act on several alternative conditions, run one removal for each. Requires a `confirmation_token` from `preview_bulk_change` — pass the same `filter` and `signup_form` here, unchanged, because the token is bound to the exact selection that was previewed and a rebuilt one is rejected. The cohort is counted again before anything is queued: if it has grown substantially since the preview, the change is refused rather than applied to people who were not in the number shown. The count from that fresh check is what is returned as `subscribers_matched`, since it names the group the job will act on, with `previewed_before` alongside it whenever that differs from the count the token was issued for. If nobody matches any more, nothing is queued and the result says so, with `subscribers_matched` of 0 — the token is spent either way. Only static segments accept manual membership changes; dynamic and pre-built segments are rule-computed and are rejected with an error. Resolve a segment name to its `segment_id` with `list_segments` first; this tool takes only the id. The work runs in the background and is NOT finished when this returns: `status` is `processing` when it begins immediately, or `waiting` when another bulk operation is already running. Subscribers already in the target state are skipped without the job failing, so a partial result is normal rather than an error. Returns a JSON object as text: `status`, `subscribers_matched` (how many matched when the work was queued — not a count of what has changed, since nothing has completed yet), a plain-language `message` describing what is happening, and the `filter` and `segment_id` echoed back so the same selection can be reused or reversed, plus any `signup_form` that was given.
bulk_remove_subscribers_from_segment
Unarchives every subscriber matching a filter, putting them back into the active subscriber count for the account so they receive sends again. The inverse of `bulk_archive_subscribers`, and the way to undo a bulk archive. Unarchiving is rate limited per subscriber to once every 45 days, counted from when that subscriber was last unarchived; the 45-day window on archiving is tracked separately and does not block an unarchive. Requires a `confirmation_token` from `preview_bulk_change` — pass the same `filter` and `signup_form` here, unchanged, because the token is bound to the exact selector that was previewed and a rebuilt one is rejected. The work runs in the background and is NOT finished when this returns: `status` is `processing` when it begins immediately, `waiting` when another bulk operation is already running, or `nothing_to_do` when the group emptied between the preview and now, in which case nothing was queued and the token is spent. Individual subscribers can be skipped without the job failing, either because they are not eligible for the change — already in the target state, or in some other status the change does not apply to — or because that subscriber is inside its own rate-limit window, so a partial result is normal rather than a failure. The cohort is counted again before anything is queued: if it has grown substantially since the preview, the change is refused rather than applied to people who were not in the number shown. Returns a JSON object as text: `status`, `subscribers_matched` (how many matched when the work was queued — not a count of what has changed, since nothing has completed yet), `previewed_before` when that differs from the count the token was issued for, so a group that moved between the preview and the write is visible rather than silently replaced, a plain-language `message` describing what is happening, and the `filter` plus any `signup_form` echoed back so the same selector can be reused.
bulk_unarchive_subscribers
Answers "how much revenue did I make", "checkout sales", "total orders", "conversion rate from visits to purchases", "Flodesk Checkout performance", "did I make money this month". Returns Flodesk Checkout performance for one currency: overall stats (totalVisitors, totalOrders, totalRevenue, conversionRate) plus per-checkout breakdown. Only the per-checkout breakdown is paginated — 10 per page by default, up to 20 via `per_page`, with `page` (1-based) to fetch more; the overall stats are always complete. Monetary amounts (totalSales, totalSubscriptionRevenue) are in the currency major unit (e.g. 10.50 = ten dollars fifty), not cents. The conversion rate is a decimal between 0 and 1 (e.g., 0.27 = 27%). Lifetime by default; pass `from`/`to` (ISO date `2026-03-01` or RFC3339) to scope sales, orders, and conversion to orders placed in that window (`to` defaults to today, max 365-day span). Use for **e-commerce, sales, and revenue questions tied to Flodesk Checkout**.
get_checkout_totals
Creates a new Flodesk segment from a name plus a set of engagement, recency, status, and signup-source conditions, then fills it once with the subscribers matching those conditions. Membership is a one-time snapshot taken at creation: the conditions choose who goes in and are not saved with the segment, so membership does not change as subscribers start or stop matching. Nothing existing is changed or overwritten. Requires a `confirmation_token` from `preview_segment_count` issued for the same `filter` and `signup_form`, so the group being saved is one whose size was reported first. Without a token, or with one issued for a different filter, account, or operation, no segment is created and nothing is queued. A token is single-use and expires after 120 seconds, so any retry — including after a name collision — needs a fresh one. Use to persist an audience for reuse — this is the only tool that saves a segment: "save these people as a segment", "create a segment of subscribers who clicked in the last 30 days", "make a segment of everyone inactive for 6 months". A request to save or set up an audience under a name is this tool, not a count and not a lookup. For the size of a filter alone, with nothing saved, use `preview_segment_count` — the same `filter` yields the same number here. `list_segments` reads segments that already exist and cannot create one, so a new segment name belongs here. To put one specific subscriber into a segment that already exists use `add_subscriber_to_segment`, and for a whole cohort into one that already exists — including refreshing a segment made here — use `bulk_add_subscribers_to_segment`. Does not rename, edit, recolor, or delete segments. Inputs: `name` (required, up to 100 characters) is the new segment's name, passed directly — no id and no lookup of existing names is needed first, because a collision comes back as an error naming it. `filter` (required) takes `conditions` as an array of 1–10 `{ field, operator, value }` objects, even for one condition; `match` joins them ('all' = AND, the default; 'any' = OR). Fields: date `createdAt`/`lastActiveAt`/`lastUnsubscribedAt` (absolute RFC-3339, or relative like "-90 days"); rate `openRate`/`clickRate` as decimals between 0 and 1 (e.g., 0.27 = 27%); engagement counts `openedEmails`/`clickedEmails`/`deliveredEmails` over a window (`_l7d`/`_l30d`/`_l90d`/`_l180d`/`_l365d`) or lifetime (no suffix); `status` (active, unconfirmed, unsubscribed, bounced, complained, cleaned, archived); and `sourceType` (signup form, manual, import, integration, checkout, product catalog — checkout and product catalog are separate values that both cover buyers, so include both with `match: "any"` to segment every buyer); and `segmentIds`, which matches subscribers already in a segment and takes ONE segment id (from `list_segments`) per condition — repeat the condition with `match: "any"` to cover several segments. Operators: `gt`/`ge`/`lt`/`le` for dates, rates, and counts (use `ge`/`le`, not `gte`/`lte`); counts also allow `eq`; `status`/`sourceType` use `eq`/`ne` only; `segmentIds` uses `contains` only. An empty `conditions` array is rejected. `signup_form` is optional and takes a form name (not an id), restricting the snapshot to subscribers who joined through that form. `confirmation_token` is the token `preview_segment_count` returns for that exact `filter` and `signup_form`, passed back unchanged; the schema marks it optional for compatibility, but a call without it is refused. Returns a JSON object as text: `segment` with the new segment id, its name, and `matchedCount` — how many subscribers matched when the fill was queued, the same frame `bulk_add_subscribers_to_segment` reports as `subscribers_matched`: what was queued, not settled membership, since the fill runs in the background and a read moments later can show fewer. A `matchedCount` of 0 is a success — the segment is created empty and nothing is queued. Plus a `message` summarizing what was created. A name already used by another segment is rejected with an error naming the collision, so a different name can be supplied. A group that has grown well past the size it was counted at is refused with nothing created and nothing queued, and so is a token that was already used, has expired, or was issued for a different filter; each says which. A group that has shrunk, or emptied entirely, is not refused. When the segment is created but its fill cannot be queued, the result is an error naming the segment and its id, and that fill is recoverable with `bulk_add_subscribers_to_segment` against that id rather than by creating the segment again. The new segment appears in `list_segments` immediately and can be looked up there by name; `inspect_segment` reads it by the returned id.
create_segment
Creates one Flodesk subscriber from a required email plus optional first and last name, and optionally assigns them to one or more existing static segments. Creation is non-destructive find-or-create: if a subscriber with that email already exists, it is returned unchanged — never overwritten, re-segmented, or merged — with `created` set to false. Use to add a new subscriber — "add [email protected] as a subscriber", "create a contact for John Doe in my VIP segment". Segment ids for `segment_ids` come from `list_segments` (resolve a segment name to its id there first); only static segments can be assigned, since dynamic and pre-built segments are filter-computed and are rejected with an error. To read an existing subscriber use `get_subscriber`; to change an existing subscriber's name or fields use `update_subscriber`; to add an already-existing subscriber to a segment use `add_subscriber_to_segment`. Inputs: `email` (required, up to 320 characters), `first_name` and `last_name` (each up to 100 characters), and `segment_ids` (up to 20 static segment ids). Returns a JSON object as text with `created` (true when newly created, false when the email already existed) and `subscriber` (id, email, firstName, lastName, status, and segments each with a name and type). A new subscriber's status is "active" by default, or "bounced" if the email is on the suppression list. When an existing subscriber is returned, an advisory `note` explains that the requested segment assignment or name was not applied.
create_subscriber
Answers "which emails/campaigns/newsletters/broadcasts performed best", "top emails by opens/clicks", "worst open rate", "rank my recent sends", "show me my top 10 emails", "which campaigns went to this segment". Returns a paginated list of individual email campaigns ranked by a chosen metric, each with its name, subject, send count, open, click and unsubscribe rates, and the recipient scope it was sent to — 10 per page by default, up to 20 via `per_page`; pass `page` (1-based) to fetch more. Open, click and unsubscribe rates are decimals between 0 and 1 (e.g., 0.27 = 27%). Each row also carries `abTest`: null for an ordinary campaign, or the split test's status and winner when that campaign ran a subject-line A/B test, so split tests can be spotted among the rows on the page being ranked rather than by checking campaigns one at a time. There is no parameter that filters or sorts by A/B outcome, and only the rows on the requested page carry it. A tested row's counts and rates are blended across both variants, plus the remainder send once that has shipped — never the winning variant alone. A row whose name ends in ` - Resend` is the exception: it reports the resend's own counts while carrying the original campaign's id, subject, and `abTest`, so its rates are NOT the split test's. Because the id is the original's, passing it to `get_email_stats` returns the original's figures rather than this row's. `recipientScope` reports who a campaign was sent to: `allSubscribers` is true for a send to everyone, `includedSegments` and `excludedSegments` list the segments addressed and held back (each with its id and current name), and `addedSubscribers`/`removedSubscribers` count people named individually. A segment deleted since the send keeps its id with a null name and `unresolved` true. A null `recipientScope` means the stored audience is unavailable — not that nobody received it. Segment names are current, so a renamed segment shows its new name. At most five segments are listed per campaign; `omittedSegments` counts any beyond that. A row whose name ends in "- Resend" carries the original send's scope. Campaigns with different recipient scopes are not like-for-like: a small curated segment and an all-subscribers send routinely differ several-fold in open rate, so a ranking or average across mixed scopes compares audiences rather than performance. The counts and rates on these rows are read from a periodically rebuilt analytics rollup rather than counted live. The rebuild runs in batches on no fixed schedule, so a figure here can trail that email's report page in Flodesk, which counts live — occasionally by a long way. For a resent email that page also adds the opens, clicks and unsubscribes the resend earned to the original's, so those counts and their rates stay above these figures even after the rollup catches up, while its send count does not grow, because a resend reaches only the original's unopened recipients — this list reports the resend as its own ` - Resend` row. Flodesk's Analytics page reads the same rollup, so for the same emails it shows the same figures. An email sent since the rollup last reached this account is not listed yet, rather than listed with zero figures. Ranks all-time by default; pass `from`/`to` (ISO date `2026-03-01` or RFC3339 `2026-03-01T00:00:00Z`) to rank only campaigns sent in that window — `to` defaults to today, max 365-day span. `segment_id` restricts the ranking to campaigns explicitly addressed to one segment; a send to all subscribers does not match even though that segment's members received it, and a campaign that excluded the segment does not match either. Segment ids come from `list_segments`. Each row also carries the design the email was built on, where the campaign records one — the template source, its identifier, and its name where that template record has a name — so emails can be grouped by template or layout without reading any email body. Use for **per-campaign rankings and comparisons**. Not for overall totals (use `get_email_totals`), change over time (use `get_email_trends`), or revenue/sales (use `get_checkout_totals` — revenue is not available per email). For one email's full detail — including per-variant A/B results — use `get_email_stats` with that email's id. For who opened or clicked a specific email, use `list_email_recipients` with that email's id. For what a specific email actually SAID — its copy, hooks and calls to action — use `get_email_content` with that email's id.
rank_emails
Answers "what did this email say", "what was the hook/opening line", "show me the copy of that newsletter", "which template was this built on". Returns one email campaign by id: its name, status, subject, preview text, send time, and the email's copy as an ordered list of blocks — first block first, so the opening hook reads first. Each block carries the kind of block it came from and its text with formatting removed; a block that links somewhere also carries the destination and the kind of link (for example an external url, an uploaded file, or a checkout). Image blocks carry their image url, and usually no text — alt text or a caption surfaces as text when the block has one, but words set inside the picture itself do not. An email whose blocks are mostly images is one whose copy is largely unreadable, not one with little to say. Also returns the design the email was built on: the template source (empty for a campaign duplicated from another), its identifier when it referenced a template at all, and the name of that template when the record has one — gallery templates do, templates a user saved themselves do not. Reaches campaigns in any status, drafts and scheduled sends included, so an unsent or past email can serve as the reference for writing the next one. Very long emails are capped: the response reports whether the block list was cut and how many content blocks the email has in total (layout-only blocks are never counted or returned). Takes the id of an email from `rank_emails`; it does not look an email up by name, subject, or recency. Use for the content of ONE email. To rank or compare emails by performance use `rank_emails` — its rows carry the same template fields, so grouping many emails by design needs no call here; for who opened or clicked an email use `list_email_recipients`; for account-wide totals use `get_email_totals`. Returns a JSON object as text.
get_email_content
Answers "how did this email do", "what were the A/B test results", "which subject line won", "was there a clear winner", "show me the split test for this campaign". Returns one sent email looked up by its id: its name, subject, send time, send count, unique opens, unique clicks, and open, click, and unsubscribe rates — plus an `abTest` object when that email ran a subject-line split test. All rates are decimals between 0 and 1 (e.g., 0.27 = 27%) and divide by messages delivered, not sent; the delivered count is not returned, so a rate cannot be re-derived from the send count beside it. Emails that did not run a split test return `abTest` as null, and carry no variant data of any kind. A `rank_emails` row whose name ends in ` - Resend` carries the ORIGINAL email's id, so passing that id here returns the original's own figures, never the resend's — a resend's counts exist only on that ranked row. Any `abTest` returned likewise describes the original's test, not one the resend ran. When present, `abTest` gives the test status, the winner, the test window, the sample size and the held-back remainder size, how many recipients have been sent to so far and how many are still pending, and one entry per variant with that variant's subject line, sends, unique opens, unique clicks, open rate, click rate, and whether it won. The top-level figures cover whatever has actually sent — the two variants combined until the held-back remainder ships, the whole send after — while the per-variant ones cover only their share of the sample. Once the remainder has shipped the top-level figures cover recipients the per-variant ones do not, so they stop matching and the blended rate can sit above, below, or between the variant rates. The two sets of figures also come from different stores: the top-level ones are read from a periodically rebuilt analytics rollup while the per-variant ones are counted live until the test completes, so the two disagreeing is expected rather than an error. The rebuild runs in batches on no fixed schedule, so the top-level figures can trail both the per-variant ones beside them and this email's report page in Flodesk, which counts live — occasionally by a long way. For a resent email that page also adds the opens, clicks and unsubscribes the resend earned to the original's, so those counts and their rates stay above these figures even after the rollup catches up, while its send count does not grow, because a resend reaches only the original's unopened recipients — `rank_emails` reports the resend as its own ` - Resend` row. Flodesk's Analytics page reads the same rollup, so for the same emails it shows the same figures. While the test is running, a gap between the top-level and per-variant figures is rollup staleness rather than a wrong figure. After the test completes the per-variant figures stop moving while the top-level ones keep counting late opens, so the gap between them is permanent rather than a staleness artifact — including on a small send that had no remainder to ship. Status is one of running, finalizing, completed, or cancelled. While a test is running its per-variant figures are live and still moving; once it is completed they are frozen to the values recorded when the test ended. Only a completed test freezes — a cancelled one keeps drifting as recipients open. The winner is A, B, `tie` when both variants performed equally, or `none` when no variant performed well enough to pick. It is absent while the test is undecided, and also on a cancelled test, which never picks one — so absence is distinct from `none`. The test runs for a fixed window — 24 hours on production — reported as `startedAt` and `endsAt`, and the winner is chosen by the higher unique open rate, provided at least one variant clears a minimum number of opens. Use when an email id is already known and the question is about that one email. When the email is identified by name, recency, or anything other than an id, use `rank_emails` first to resolve the id — its rows carry an `abTest` status and winner, so split tests can also be spotted there without checking each email. For account-wide totals use `get_email_totals`, for performance over time use `get_email_trends`, and for who opened or clicked use `list_email_recipients`. Returns a JSON object as text.
get_email_stats
Answers "how many emails did I send", "what's my overall open/click rate", "email performance summary". Returns aggregate totals across all your campaigns: the number of emails sent, total sends, and overall delivery, open, click, and unsubscribe rates. Delivery, open, click, and unsubscribe rates are decimals between 0 and 1 (e.g., 0.27 = 27%). Totals are lifetime by default; pass `from`/`to` (ISO date `2026-03-01` or RFC3339 `2026-03-01T00:00:00Z`) to scope the totals and rates to emails sent in that window — `to` defaults to today, max 365-day span. These totals blend campaigns sent to different recipient scopes — small curated segments alongside all-subscriber sends — so they are an account-wide average rather than a like-for-like rate. `rank_emails` reports each campaign's recipient scope. These figures are read from a periodically rebuilt analytics rollup rather than counted live. The rebuild runs in batches on no fixed schedule, so these totals can trail each email's report page in Flodesk, which counts live — occasionally by a long way. Flodesk's Analytics page reads the same rollup, so for the same emails it shows the same figures. Use for **single overall numbers**. Not for per-campaign rankings (use `rank_emails`) or change over time (use `get_email_trends`).
get_email_totals
Answers "how has my open rate changed", "email performance over the last 6 months", "trend in sends/clicks/unsubscribes", "is engagement going up or down", "monthly/weekly email stats", "how did my newsletter segment do in May". Returns a time-series of campaign metrics (totalEmails, totalSends, totalUniqueOpens, totalUniqueClicks, openRate, clickRate, unsubRate) bucketed by day, week, or month over a date range. Open, click, and unsubscribe rates are decimals between 0 and 1 (e.g., 0.27 = 27%). Set `rank_by_metric: true` with a `metric` to also get a `rankings` list of the top campaigns by that metric over the same range (page size via `per_page`, 1–20, default 10). Rankings are ordered by the requested metric, highest first, for the six metrics that rank by value — openRate, clickRate and unsubRate included. A campaign whose value for that metric is zero is left out, so a rate ranking lists only campaigns that recorded that rate. `totalEmails` returns a list too, but it is the most recently sent campaigns with resends excluded, not a ranking by any count. Every ranked row carries the same fields whatever metric was ranked by: the campaign id and name, when it was sent, its totalSends, totalUniqueOpens and totalUniqueClicks, and its openRate, clickRate and unsubRate as decimals between 0 and 1 (e.g., 0.27 = 27%). Each bucket blends whatever campaigns were sent in it, which may address different recipient scopes — a curated segment in one month and all subscribers in the next moves a rate without any change in performance. Neither the series nor its `rankings` rows carry recipient scope; `rank_emails` reports it per campaign. The series and any `rankings` rows are read from a periodically rebuilt analytics rollup rather than counted live. The rebuild runs in batches on no fixed schedule, so the most recent buckets and the rankings can trail each email's report page in Flodesk, which counts live — occasionally by a long way. Flodesk's Analytics page reads the same rollup, so for the same emails it shows the same figures. Set `segment_id` to scope the series — and the `rankings` list, when one is requested — to the campaigns whose audience included that segment. A scoped request's date range must not exceed 365 days. Those campaigns are counted in full: every send, open, click and unsubscribe they produced is included, including the ones that went to other segments the same campaign also targeted. The scoped figures therefore describe whole campaigns that reached the segment, not the segment members' own activity. A campaign that named the segment only as an exclusion, or that went to the whole list with no segment conditions at all, is not counted. A scoped response also carries a `segment` object with that segment's id, name, and `currentActiveSubscribers` — the segment's current stored size, not a figure for the requested window. It is an as-of tally read from stored membership and never recomputed for this call, so a subscriber added or removed during this conversation may not be reflected yet. It is `null` when no usable stored tally is available — ordinarily a rule-based `dynamic` segment, whose membership is evaluated live rather than stored; `null` means the size is unknown, not zero. `list_segments` resolves a segment name to the id this takes; an id the account cannot see comes back as an error rather than an empty series. Use for **change over time and chart data**. Not for current totals (use `get_email_totals`) or ranking individual campaigns (use `rank_emails`).
get_email_trends
Returns the subscribers who opened, or who clicked, one specific email — a capped list of recipient rows plus the true total count of all such recipients, so results read as "X of N" and never as the complete set. Set `engagement` to 'opened' or 'clicked' (required; one facet per call). Pass `email_id` as the id of an email from `rank_emails`; this tool does not look an email up by name or recency — use `rank_emails` to resolve "my last / latest email" to an id. For clicks, the response also lists which links were clicked (each link with its url and unique/total click counts), and an optional `link` (a url, valid only with `engagement: "clicked"`) narrows the list to the subscribers who clicked that exact link. `limit` is 1–100 (default 25); the response always carries the full `total` independent of the limit. Use for "who opened my last newsletter", "who clicked the link in my sale email", "which links got clicked and by whom". For subscribers matching attributes across all emails (engagement, recency, signup source) use `list_subscribers`; to rank or compare emails by performance use `rank_emails`; for one known subscriber's full profile use `get_subscriber`. Returns a JSON object as text: `total` and `recipients` — each row with id, email, first and last name, status (active, unconfirmed, unsubscribed, bounced, complained, cleaned, or archived), and either the most recent open time and open count, or the most recent click time and click count. For clicks without a specific `link` it also returns `links` — up to the 50 most-clicked links, each with its url and unique and total click counts — and `totalLinks`, the true count of distinct links clicked (greater than the `links` length when the list is a top slice). When a `link` is given that url is echoed back.
list_email_recipients
Returns a brief overview of what this Flodesk MCP connector can answer: email performance, subscribers, workflows, signup forms, and checkout/revenue — with the specific tool to call for each. Use for first-turn discovery and vague status queries like "what can I ask about my Flodesk", "what data is available", "help", "show me my Flodesk stats", "how's my account doing", "give me a rundown", "dashboard", "overview", "summarize everything". Takes no inputs (any provided arguments are ignored). Returns a markdown routing guide as text; does not return account data. For specific questions, call the matching tool directly (e.g. `get_email_totals`, `rank_emails`, `get_email_trends`, `get_subscriber_totals`, `get_subscriber_trends`, `list_segments`, `rank_segments`, `rank_workflows`, `rank_forms`, `get_checkout_totals`) rather than this overview.
flodesk_overview
Answers "form signup performance", "which form converts best", "opt-in rates", "how many people signed up via my forms", "best form on my site", "compare my opt-in forms". Returns overall form opt-in summary (totalVisitors, totalOptIns, optInRate) plus a per-form breakdown ranked by a metric. Only the per-form breakdown is paginated — 10 per page by default, up to 20 via `per_page`, with `page` (1-based) to fetch more; the overall summary is always complete. The opt-in rate is a decimal between 0 and 1 (e.g., 0.27 = 27%). Lifetime by default; pass `from`/`to` (ISO date `2026-03-01` or RFC3339) to scope visitors and opt-ins to that window (`to` defaults to today, max 365-day span). Use for **lead-capture and signup conversion analysis**.
rank_forms
Removes one existing Flodesk subscriber, identified by exactly one of `email` or `id`, from one existing static segment identified by `segment_id`. This is a destructive membership write: it removes the subscriber's membership in that segment only. It never deletes the subscriber and never deletes the segment — removing a subscriber from its last segment leaves the subscriber intact. Use to take ONE named subscriber out of a group — "remove [email protected] from my VIP segment". To remove everyone matching a filter instead of a named person, use `bulk_remove_subscribers_from_segment`. Resolve a segment name to its `segment_id` with `list_segments` first; this tool takes only the id, and only static segments support manual membership (dynamic and pre-built segments are rule-computed and are rejected with an error). For the reverse — adding a subscriber to a group — use `add_subscriber_to_segment`; moving a subscriber between groups is a removal here followed by an add there. Pass exactly one of `email` or `id` (supplying both or neither returns an error), plus `segment_id`. Returns a JSON object as text with `removed` (true when the membership was removed, false when the subscriber was not in the segment — both are success), `subscriber` (id, email), `segment` (id, name), and a short `message` summarizing what happened.
remove_subscriber_from_segment
Returns the count of subscribers matching a set of engagement, recency, status, and signup-source conditions — a single number, with no subscriber rows and no personal data. Use it for the size of an audience when nothing is saved or changed: "how many people opened something in the last 30 days", "how many subscribers would this filter select". It answers how many, never which people: a request to find, show, or list the matching subscribers themselves is `list_subscribers`, even when the aim is to remove or archive them afterwards. A request to save the audience under a name is `create_segment`, not this tool. Pass `filter.conditions` as an array of 1–10 `{ field, operator, value }` objects, even for one condition; `match` joins them ('all' = AND, the default; 'any' = OR). Fields: date `createdAt`/`lastActiveAt`/`lastUnsubscribedAt` (absolute RFC-3339, or relative like "-90 days"); rate `openRate`/`clickRate` as decimals between 0 and 1 (e.g., 0.27 = 27%); engagement counts `openedEmails`/`clickedEmails`/`deliveredEmails` over a window (`_l7d`/`_l30d`/`_l90d`/`_l180d`/`_l365d`) or lifetime (no suffix); `status` (active, unconfirmed, unsubscribed, bounced, complained, cleaned, archived); and `sourceType` (signup form, manual, import, integration, checkout, product catalog — checkout and product catalog are separate values that both cover buyers, so include both with `match: "any"` to count every buyer); and `segmentIds`, which matches subscribers already in a segment and takes ONE segment id (from `list_segments`) per condition — repeat the condition with `match: "any"` to cover several segments. Operators: `gt`/`ge`/`lt`/`le` for dates, rates, and counts (use `ge`/`le`, not `gte`/`lte`); counts also allow `eq`; `status`/`sourceType` use `eq`/`ne` only; `segmentIds` uses `contains` only. An empty `conditions` array is rejected. `signup_form` takes a form name (not an id) and restricts the count to subscribers who joined through that form. To see *who* matches (the actual rows) use `list_subscribers`; for the unconditional total number of active subscribers use `get_subscriber_totals`; to save this filter as a segment use `create_segment`. Returns a JSON object as text: `count` (the true number of matching subscribers, 0 when none match) and `filter` (the `conditions` and `match` echoed back), plus a top-level `signup_form` when supplied, so the same filter can be reused to create a segment. When `count` is above zero the result also carries `confirmation_token` and `expires_in_seconds`: a single-use token pinning that exact filter and form, which `create_segment` requires before it will create and fill a segment for this group. The token is valid only for the same filter passed back unchanged, and only for creating a segment — it authorizes no other change. A `count` of 0 returns no token and a `message` saying so, because there is nothing to save. When no token can be issued the count is still returned, with a `message` saying so, and a segment cannot be created from that group until a later count returns one.
preview_segment_count
Returns one segment's definition and size, looked up by segment id: its name, its type (`static`, `dynamic`, or `pre_built`), the filter expression that defines it for rule-based segments, and its total and active subscriber counts (each a whole number or `null`, never a rate). The filter expression is returned verbatim as stored, in the expression syntax segment rules are written in — it is not parsed, translated into conditions, or rewritten. A `static` segment carries no filter expression at all, because its membership is a fixed list rather than a rule. Use when a segment id is already known — "what does segment 68b1f4c39b2e7d0011ac5e21 filter on", "how big is segment 68b1f4c39b2e7d0011ac5e21", "is segment 68b1f4c39b2e7d0011ac5e21 rule-based or a fixed list". To find a segment id by name, or to see every segment in the account, use `list_segments` — this tool reads one segment already identified by id, and is the only one that returns the filter expression. The counts are read from stored membership and are never recomputed for this call. Either count is `null` when no usable stored membership tally is available — ordinarily a `dynamic` (rule-based) segment, whose membership is evaluated live rather than stored, or a `pre_built` segment whose membership has not been calculated yet. A `null` count means the size is unknown, not zero; a count of 0 means the tally really is zero. A count that is present is an as-of figure: a `pre_built` segment's is as of the last time its membership was calculated, and a `static` segment's as of the last time its membership was tallied, so a subscriber added or removed during this conversation may not be reflected yet. `list_segments` reports the same active-subscriber count for the same segment. To count how many subscribers match a rule right now, use `preview_segment_count` with a filter composed for that request. Input: `segment_id` (required), the id of one segment. Returns a JSON object as text with the segment's id, name, type, filter expression (absent for `static` segments), total subscriber count, and active subscriber count (each `null` when not computed), where active means the subscriber's status is active.
inspect_segment
Answers "how many people are in both segment X and segment Y", "do these two segments overlap", "how many subscribers downloaded both", "how much do my segments share", "are these audiences the same people". Returns `overlapCount` — the number of active subscribers who belong to every segment named — and `segments`, one row per input with `id`, `name`, `segmentType`, and `totalActives`, so the overlap can be read as a share of each. Counts are computed live, so they can differ slightly from the as-of counts `list_segments` reports. Takes 2–10 segment ids from `list_segments`, and covers static segments only — a dynamic or pre-built segment is rejected by name rather than counted as zero. Use for **membership overlap between segments**. Not for ranking segments by size or engagement (use `rank_segments`), one segment's definition and size (use `inspect_segment`), or listing the people themselves (use `list_subscribers`).
get_segment_overlap
Returns subscribers matching a set of engagement, recency, status, and signup-source conditions — a capped list of matching rows plus the true total count of all matches, so results read as "X of N" and never as the full audience. Pass `filter.conditions` as an array of 1–10 `{ field, operator, value }` objects, even for one condition; `match` joins them ('all' = AND, the default; 'any' = OR). Fields: date `createdAt`/`lastActiveAt`/`lastUnsubscribedAt` (absolute RFC-3339, or relative like "-90 days"); rate `openRate`/`clickRate` as decimals between 0 and 1 (e.g., 0.27 = 27%); engagement counts `openedEmails`/`clickedEmails`/`deliveredEmails` over a window (`_l7d`/`_l30d`/`_l90d`/`_l180d`/`_l365d`) or lifetime (no suffix); `status` (active, unconfirmed, unsubscribed, bounced, complained, cleaned, archived); and `sourceType` (signup form, manual, import, integration, checkout). Operators: `gt`/`ge`/`lt`/`le` for dates, rates, and counts (use `ge`/`le`, not `gte`/`lte`); counts also allow `eq`; `status`/`sourceType` use `eq`/`ne` only. At least one condition is required. `signup_form` takes a form name (not an id) and restricts results to subscribers who joined through that form. `limit` is 1–100 (default 25); the response always carries the full `total` independent of the limit. For "most engaged" or "most active" subscribers, sort by a recent-engagement count: set `sort` to `openedEmails_l30d`/`_l90d`/`_l365d`/`openedEmails` (or the clicked equivalents) with `direction: "desc"`. This ranks by engagement count, not by open/click rate, so pair it with a `deliveredEmails_*` floor to avoid surfacing very-low-volume subscribers. `sort` defaults to `lastActiveAt` (most recent activity). Use for audience questions — "who are my most engaged subscribers", "who hasn't opened anything in 6 months", "who signed up this month from [form]", "help me clean my list". To rank emails use `rank_emails`; for who opened or clicked one specific email use `list_email_recipients`; for a single known subscriber's full profile use `get_subscriber`. Returns a JSON object as text: `total` and `subscribers`, each row with id, email, first and last name, status, source (how they joined, with the signup-form name where applicable), when they were added, and the timestamps of their most recent email open and link click (null if they never opened or clicked).
list_subscribers
Answers "how has my list grown", "subscriber growth over time", "new subscribers per month", "unsubscribe trend", "is my audience growing or shrinking", "list growth chart". Returns a time-series of subscriber-list metrics (totalActives, totalNew, totalUnsubscribed, and net growth = new minus unsubscribed) bucketed by day, week, or month over a date range. Use for **list growth over time and chart data**. Not for current totals (use `get_subscriber_totals`) or per-segment breakdowns (use `rank_segments`).
get_subscriber_trends
Answers "how many subscribers do I have", "list size", "new subscribers this week", "subscriber growth snapshot". Returns a snapshot of the subscriber list: the current number of active subscribers, plus how many were added in the last 7 and 30 days. Pass `period` (last7Days or last30Days) — or, alternatively, a `from`/`to` window (ISO date `2026-03-01` or RFC3339; `to` defaults to today, max 365-day span) — to also get new/unsub counts and the change vs the prior period. Pass `period` or `from`/`to`, not both. Use for **current list size and recent growth**. Not for change over time (use `get_subscriber_trends`) or per-segment breakdowns (use `rank_segments`).
get_subscriber_totals
Returns one Flodesk subscriber's profile, looked up by an exact email or id: their stable subscriber `id`, email, first and last name, status (active, unconfirmed, unsubscribed, bounced, complained, cleaned, or archived), when they were added, how they joined (source), their segment memberships (each marked static or dynamic — legacy groups appear as static segments), and the timestamps of their most recent email open and most recent link click (null if they never opened or clicked). Pass exactly one of `email` or `id`; supplying both or neither returns an error, as does an unknown subscriber. Set `include_custom_fields: true` to also return the subscriber's custom field values, which are omitted by default. The `id` is the same identifier `list_subscribers` returns and that id-based tools accept, so an email lookup yields the subscriber's id in one call. A `list_subscribers` row already carries the rest of this profile — id, email, first and last name, status, source, when they were added, and the last open and click timestamps — and carries segment memberships too when it is called with `include_segments: true`. Custom field values are the one thing a row cannot carry; past those, a lookup per row after a list adds little, differing only in that the open and click timestamps here are recomputed live rather than read from the rollup the rows are served from. Use for questions about a single known subscriber — "what's the status of [email protected]", "when did this subscriber last open or click", "which segments is this subscriber in", "how did they sign up". For the authenticated caller's own account use `get_me`. This tool resolves exactly one subscriber by email or id; it does not search, filter, or list. Returns a JSON object as text.
get_subscriber
Answers "biggest segment", "segments ranked by size/engagement", "which audience is most active", "compare my segments". Returns a paginated list of subscriber segments ranked by a metric, each with id, name, subscriber count, and engagement stats — 10 per page by default, up to 20 via `per_page`; pass `page` (1-based) to fetch more. Open and click rates are decimals between 0 and 1 (e.g., 0.27 = 27%). Rates are lifetime by default; pass `from`/`to` (ISO date `2026-03-01` or RFC3339) to scope openRate/clickRate to engagement in that window (`to` defaults to today, max 365-day span) — the subscriber count stays current. Use for **ranking and comparing segments**. To simply list your segments or look up a segment id by name, use `list_segments`.
rank_segments
Lists your subscriber segments, each with its id, name, type, and active-subscriber count. Every segment type is listed, and a segment saved moments earlier by `create_segment` is already here, so this is the tool for resolving a segment name to its id before calling another segment-scoped tool. For ranking segments by size or engagement, or comparing their open/click rates, use `rank_segments`. `page` is the 1-based page index (default 1). Page size is fixed at 100 segments and is not an input, so the offset is `(page - 1) * 100` and `page: 2` returns the 101st–200th most recently created segments; an account of any size is reachable page by page. Returns a JSON object as text with a `segments` list, a `total`, the `page` served, and `hasMore`; each entry has the segment id, its name, its type (`static`, `dynamic`, or `pre_built`), and its active-subscriber count (a whole number, not a rate). Segments are ordered newest first. `hasMore` is true when segments beyond this page exist and false on the last page; `total` is the full count of segments on the account and is identical on every page. A page past the last one returns an empty `segments` list with that same `total` and `hasMore` false. Paging reads the live roster rather than a snapshot, so a segment created or deleted between two page requests can shift an entry across a page boundary. An active-subscriber count is `null` when no usable stored membership tally is available — ordinarily a `dynamic` (rule-based) segment, whose membership is evaluated live rather than stored, or a `pre_built` segment whose membership has not been calculated yet. A `null` count means the size is unknown, not zero; a count of 0 means the tally really is zero. To count how many subscribers match a rule right now, use `preview_segment_count`.
list_segments
Sets one Flodesk subscriber's status to unsubscribed, identified by exactly one of `email` or `id`, so they stop receiving the account's emails. This is destructive and cannot be reversed from this connector — the subscriber must opt back in through Flodesk. Active and archived subscribers can be unsubscribed; one whose status is already unsubscribed, bounced, complained, cleaned, or unconfirmed is left unchanged and reported with `changed: false` and their actual status. To edit profile fields (name, custom fields) use `update_subscriber` — it never changes subscription status; to remove someone from a segment without unsubscribing them use `remove_subscriber_from_segment`. Returns a JSON object as text with `changed`, `subscriber` (id, email, status), and a short `message` describing what happened.
unsubscribe_subscriber
Updates an existing Flodesk subscriber's editable profile fields — first name, last name, and/or custom fields — identified by exactly one of `email` or `id`. The update is partial and overwrites existing values: only the fields you supply change; an omitted field is left unchanged, and an explicit empty string clears that field. It never creates a subscriber, never changes their email/identity, never changes their subscription status, and never directly changes segment membership (a profile edit can still move them in or out of a dynamic segment, which the returned `segments` reflect). Use to correct or set profile data — "update [email protected]'s first name to Jane", "set the 'company' custom field to Acme". Custom fields are keyed by field key and set to string values; unknown or not-owned keys are rejected with an error naming each bad key (read a subscriber's existing keys with `get_subscriber` and `include_custom_fields: true`). To create a new subscriber use `create_subscriber`; to read one use `get_subscriber`; to unsubscribe use `unsubscribe_subscriber`; to change segment membership use `add_subscriber_to_segment` / `remove_subscriber_from_segment`. Pass exactly one of `email` or `id` (both or neither is an error) and at least one of `first_name`, `last_name`, or `custom_fields`. Returns a JSON object as text with `subscriber` (id, email, firstName, lastName, status, segments each with name and type, and customFields) and a `message` naming each changed field and its new value.
update_subscriber
Returns the links clicked in one automation workflow — each url with the number of distinct subscribers who clicked it and the total number of clicks, plus the true count of distinct clicked links. Answers "which links get clicked in my welcome sequence", "what is the most-clicked link in this automation", "which links were clicked in the second email of my onboarding workflow". The rows come back already ranked by total clicks, so this is the tool for ranking the links inside a workflow — `rank_workflows` ranks whole workflows against each other and never returns their links. Counts are aggregated across all of the workflow's emails unless `email_id` narrows them to one, so comparing one email's links against another's means one call per email. `workflow_id` is required and is the id of a workflow from `rank_workflows`. `email_id` is optional and narrows the links to one workflow email; that id comes from `rank_workflows` called with `workflow_id`, where each email-send node carries its own email id. Neither a workflow nor a workflow email can be looked up by name here. Counts are lifetime and take no date range. Only links that were actually clicked appear — a link present in the email that nobody clicked is not a row and is not counted, so this reports click behavior rather than the email's link inventory. Returns a JSON object as text: `links`, up to 50 rows ordered by total clicks descending, each with its url and its unique and total click counts, and `totalLinks`, the true number of distinct clicked links independent of that cap — when `totalLinks` is greater than the number of rows returned, the list is the top slice and not the complete set. Both click counts are whole numbers — a count of subscribers and a count of clicks — never rates or percentages. Urls are returned normalized, with per-subscriber tracking parameters collapsed, so repeated sends of one link aggregate into a single row. For a workflow with more than 200 distinct clicked links the ranking is taken over a subset of them. A workflow with no recorded clicks, and an id that matches nothing, both return an empty list rather than an error. Subscriber identities are never returned: for who opened or clicked one campaign email use `list_email_recipients`. To rank or compare workflows, or to read a workflow's steps with their per-node open and click counts, use `rank_workflows`.
list_workflow_links
How do I improve a ChatGPT Plugin's discoverability?
The levers are the listing surface agents actually read: names, descriptions, keywords, tool metadata, and registry health. Which lever matters depends on where discovery breaks, which is what continuous measurement shows.
What are Flodesk alternatives on ChatGPT?
As of 2026-09-28, Flodesk competes with ActiveCampaign, Aivie, AWeber, beehiiv, Bizgo, Brevo, Conversion, Customer.io, Intuit Mailchimp, Kanal, Klaviyo, Knock, L Message(エルメ), Life Analytics Mail, Loops, magnews, MailerLite, Mailrith, MailSenpai, Nitrosend - AI Native Email, Notifly, Omnisend, OneSignal, Resend, SAMWAD, Sendly, Sent, Spoki, subscline, Systeme.io, Textmagic, TrueDialog, VerticalResponse, Yournotify in ChatGPT Email & SMS Lifecycle Marketing Automation, ranked by public Discoverability Score.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.