bulk_edit_variants
ChatGPTApply the same edit to many MCP-managed variants at once. Pass targets (page_uuid + variant letter) and ops, an ordered list of edits to apply identically to every target. An op is either a replace (literal, non-regex find/replace on the html, css, or js stream) or a selector op (CSS selector + action — set_text, set_html, set_attribute, set_style, add_class, remove_class — applied to matching elements in the html stream). For a per-target value (e.g. each page's own product name), give the op a value_map (page_uuid -> value) instead of a single value; targets missing from the map are left untouched. Selector ops that don't match a given target's markup are a no-op, so stack several to handle mixed markup across the set. dry_run defaults to true: it reports match counts per target without writing anything, so you can preview and see which targets a change actually hits. Re-run with dry_run: false to stage the changes; nothing goes live until each page is published (publish_page), so a bad batch is recoverable by simply not publishing. Only MCP-managed variants are edited — a hand-built Unbounce-builder variant is reported skipped, not failed, so one bad target never aborts the batch. To swap in a new image, upload it first (upload tool) and use its upload://<handle>/<path> reference as an op value (e.g. set_attribute src); it's rehosted to a client-owned CDN URL at apply time. upload:// refs must point at image/font files and only work in html/css ops; a bad ref fails the whole call up front. Referenced images/fonts are rehosted and external refs pre-flighted the same way as update_variant_from_html. Content is read and written server-side and never passes through this conversation. Very large batches run until a wall-clock budget; anything not reached comes back under not_processed (with stopped_early: true) so you can re-run for just those. Dynamic Text Replacement (<ub:dynamic>) is a paid feature — if the account isn't entitled, a target whose edit would introduce the tag is reported as an error, not applied, without affecting the rest of the batch.
create_ai_builder_page
ChatGPTCreate a new, unpublished AI Builder page in a client from an already-uploaded editor document. The result is a normal AI Builder page: the user can open and edit it in the Unbounce AI builder, and the Classic HTML tools (update_variant_from_html and the like) do not apply to it. document_ref is an upload:// reference (from upload) to a JSON file holding the editor document — GrapesJS project data, the same shape get_ai_builder_document returns as document. Author it with get_ai_builder_catalog first: use only its component types, and style with its style-guide variables rather than hard-coded values. The document must serialize to at most 1,000,000 bytes. The document is validated against the client's catalog and style guide BEFORE anything is created: unknown component types, undefined style variables, event-handler attributes (on*), component script and javascript: URLs fail the call with every problem listed, and no page is left behind. To run code on the page, add a script node or Custom HTML deliberately. limits (on success and on a validation failure) names what the check does not cover, so a pass is not a guarantee the page renders as intended. An account whose plan lacks AI Builder (builders.ai_builder false in list_accounts_and_clients) is refused before anything is uploaded, naming create_page_from_html when the account has the Classic Builder. The page is pinned to a style guide at creation — the only moment one can be set. Omit style_guide_id to use the client's resolved style guide; the call fails, creating nothing, when none resolves. Returns the page uuid (page_id), variant_id, the pinned style_guide_id, and editor_url / overview_url for the user. The page is left unpublished with no URL. If the page is created but its content cannot be written, the call fails naming the page uuid and variant: that page has no content, cannot be published until content is written, and should be removed with delete_page before retrying.
create_page_from_html
ChatGPTCreate a new, unpublished Unbounce landing page in a client from already-uploaded HTML/CSS/JS, as an MCP-managed page — it can only be edited afterwards with these MCP tools, never in the Unbounce builder, so don't suggest the builder to the user. Pass upload:// references from upload: html_ref (body-content HTML for the page's Custom HTML block), optional css_refs (concatenated into the page stylesheet), and optional scripts (each becomes a custom-JavaScript element — a .js file, an external src include, or verbatim <script> markup, each with its own name and placement). Referenced images and fonts are rehosted as client-owned Unbounce assets and the markup rewritten to the resulting CDN URLs — relative references resolve against the HTML file's folder in the same upload bundle, large embedded data URIs are extracted, and external http(s) URLs are left untouched. To reuse an asset already in the client's library instead of uploading a duplicate, reference its cdn_url from list_assets; assets this tool does upload are deduplicated against the library too. A <form> in the HTML is automatically wired for lead capture, pointed at Unbounce's submission gateway. It must be well-formed — one <form> with named fields and a submit button, no pageId/pageVariant fields, no onsubmit/onclick handlers — or the call fails up front listing every issue. A successful submit shows a default "Thank you!" confirmation dialog; to customize the post-submit experience (message, redirect, hiding the form), register a window.ub.hooks.afterFormSubmit callback in a scripts entry or an inline <script> in the body HTML, which suppresses the default dialog. The page is created in the given numeric context (client) and left unpublished — no URL is assigned. Surviving external references are pre-flight checked for reachability (check_external_refs, default fatal): a dead Unbounce-hosted asset URL fails the call up front (it would abort publish later); dead third-party URLs come back as unreachable_refs warnings instead. Head metadata tags in the HTML (<title>, meta description/keywords/robots, og:*, favicon) are lifted out and applied as real page metadata, reported under head_metadata (manage it afterwards with set_page_metadata); tags with no platform slot (twitter:*, canonical, hreflang) come back as warnings. Dynamic Text Replacement (<ub:dynamic>) is a paid feature — HTML containing it is rejected up front if the account isn't entitled (check with get_account_plan). File bytes are read server-side and never pass through this conversation. Returns the new page's numeric id and variant id (feed the variant id to get_variant), plus the list of created assets.
create_variant_from_existing_variant
ChatGPTClone an existing Unbounce page (one built in the Unbounce builder, not through MCP) into an MCP-managed variant, as an exact copy — a one-way, one-time conversion (edit the result afterwards with these MCP tools only, never in the Unbounce builder). Pass the numeric source_variant_id (from list_page_variants) and its context (client id). Omit target_page to create a NEW page (the source becomes variant a); pass a target_page pageUuid to add the copy as a new staged challenger variant on that existing MCP page. The source is rendered and transformed to MCP-format HTML/CSS; a lead form's confirmation subpage carries over as an embedded modal, and Dynamic Text (<ub:dynamic>) survives. If the source is already MCP-managed, nothing is created and the tool reports it. Autoscale (fixed-width) source pages convert too: the clone carries the per-breakpoint layout and renders faithfully at each breakpoint, but runtime viewport scaling (--scale) is not preserved — the conversion report notes this and points to mcp-modernize for a fully responsive rewrite. The result is created UNPUBLISHED — assign a URL/traffic and publish_page to go live. To copy a whole multi-variant page, call once per variant (first with no target_page, then with the returned page uuid) and set traffic/goals afterwards. Returns the new page/variant identity plus a conversion report. The source's scripts carry over as custom-JavaScript elements, and get_variant hands every one of them back as a scripts entry: an editable js file (ref), an external include (src), or verbatim markup (tag). Re-send them on update_variant_from_html to keep them, or omit scripts entirely to leave them untouched.
create_variant_from_html
ChatGPTAdd a new variant to an existing Unbounce landing page (by page_uuid from list_client_pages) from already-uploaded HTML/CSS/JS, as an MCP-managed variant — editable afterwards with these MCP tools only, never in the Unbounce builder (don't suggest the builder to the user). Pass upload:// references from upload: html_ref (body-content HTML for the variant's Custom HTML block), optional css_refs (concatenated into the variant stylesheet), optional scripts (each becomes a custom-JavaScript element — a .js file, an external src include, or verbatim <script> markup, each with its own name and placement), and a required variant_name. Referenced images and fonts are rehosted as client-owned Unbounce assets and the markup rewritten to the resulting CDN URLs (relative refs resolve against the HTML file's folder in the same upload bundle; large data URIs are extracted; external http(s) URLs are left untouched). Reference an existing asset's cdn_url from list_assets to reuse it instead of uploading a duplicate — uploaded assets are deduplicated against the client's library too. A <form> in the HTML is automatically wired for lead capture, pointed at Unbounce's submission gateway. It must be well-formed — one <form> with named fields and a submit button, no pageId/pageVariant fields, no onsubmit/onclick handlers — or the call fails listing every issue. A successful submit shows a default "Thank you!" confirmation dialog; to customize the post-submit experience (message, redirect, hiding the form), register a window.ub.hooks.afterFormSubmit callback in a scripts entry or an inline <script> in the body HTML, which suppresses the default dialog. The new variant is added as a CHALLENGER and STAGED: it takes no traffic until set_traffic gives it a share, and nothing goes live until publish_page. Adding a variant runs an A/B test, a paid feature — the call is rejected up front if the account isn't entitled (check with get_account_plan). Dynamic Text Replacement (<ub:dynamic>) is a paid feature too, gated the same way. File bytes are read server-side and never pass through this conversation. Returns the page uuid, the client context, a variant object (numeric id, letter a/b/c, and standing — normally challenger, or discarded with a note if it couldn't be promoted), and the list of created assets.
delete_page
ChatGPTDelete an Unbounce page by its page_uuid (from list_client_pages) — deletes one page per call. The page stops serving and disappears from the account immediately, with no publish step. There is NO restore through these tools or the Unbounce app: the platform soft-deletes, and only Unbounce Support can recover a deleted page, on request. To take a page offline reversibly, use unpublish_page instead. Returns { ok: true, page_uuid, … } on success, or { ok: false, code, reason, remedy } so you can correct the inputs and retry.
delete_page_group
ChatGPTDelete a client's page group (a named folder), by group_uuid from list_page_groups. Only the group is removed — its pages are NOT deleted; they remain and still appear under "All Pages". Use manage_page_group with remove_pages first if you only want to take pages out of a group without deleting it. Returns { ok: true, group_uuid, … } on success, or { ok: false, code, reason, remedy }.
delete_variant
ChatGPTPermanently delete one of an Unbounce page's variants, by page_uuid (from list_client_pages) and variant letter a/b/c (from list_page_variants). This is the HARD removal — the variant and its history are gone and CANNOT be recovered. To pull a challenger out of the test reversibly (keeping it), use set_variant_standing with standing discarded instead. The deletion is STAGED in the page's draft and only takes effect when the page is (re)published (publish_page). Returns { ok: true, … } on success, or { ok: false, code, reason, remedy } so you can correct the inputs and retry.
download
ChatGPTDownload objects from your server-side workspace WITHOUT routing their bytes through this conversation. Pass the upload://<handle>/<relpath> references returned by upload. Returns one temporary download URL per reference; run the printed curl -o <localfile> '<download_url>' for each (no auth header needed — auth is baked into the URL). The bytes stream straight from storage to disk, never through the model. Can't run shell/curl? Use download_inband instead (small text files only).
download_inband
ChatGPTRead objects from your server-side workspace, returning their content INLINE in this tool's result. Use this ONLY if you cannot run shell/curl commands locally; if you can, prefer download, which streams the bytes to disk without passing them through this conversation. Pass the upload://<handle>/<relpath> references returned by upload, upload_inband, or get_variant. Only small text objects can be returned this way — a binary object (image/font) or one over 2 MiB is refused, with guidance to use download instead. Any refused reference fails the whole call, so retry with just the good ones.
edit_ai_builder_page
ChatGPTMake targeted changes to an existing AI Builder page's variant — insert, update, delete, retext, restyle or move components — without re-authoring the whole document. Read the page with get_ai_builder_document first to see its components. operations (1 to 50) run in order as ONE atomic batch: if any operation cannot be applied, nothing is written and the error names the operation's index. Each names components by { id } (attributes.id — it must match exactly one component; few components carry one) or { path } (the position, e.g. pages[0].frames[0].component.components[2], as the document and validation errors show it). A path is resolved against the document as earlier operations in the batch left it. - insert { parent?, at?, component } — add a component object (default parent: the first page's root; default position: last). - update { target, attributes?, classes?: { add?, remove? }, components? } — merge attributes (null removes one), add/remove classes, or replace ALL children. - delete { target }. - update_text { target, text } — plain text of a text node, of a component whose content is text, or of a component with a single text node child. On a component that shows its text in a descendant (a link button's label, a form button's editable child), it sets that descendant. Many components render visible values from their own data-* attributes (a button's text from data-label, an accordion's question from data-question, an image's alt from data-alt), and the AI builder rebuilds those values from the attributes whenever it loads the page. So an edit keeps the two in step: changing a rendered value also sets the data-* attribute on the node or its component ancestors that held the old value, and setting a data-* attribute also sets the text and attributes beneath it that held the old value. Every value changed this way is listed in synced. When two data-* attributes on one component hold the same value, which one the text comes from cannot be told, so the edit is refused; set the attribute you mean together with its text, using update with both attributes and components. Values an attribute stores as a list (pricing features, breadcrumb items, tabs, social links) are not matched: change those by setting the attribute with update. - update_style { target, style } — merge CSS into the component's own #id style rule; a component without an attributes.id is given one, reported in generated_ids. - reorder { target, to, at? } — move a component under to at position at. Components are objects, never HTML strings; use get_ai_builder_catalog for valid types and style variables. The edited document is validated against the client's catalog and style guide before anything is written (limits names what that check does not cover). An event-handler attribute (on*) or javascript: URL the batch adds is refused; one the page already carried is left alone. To run code, add a script node or Custom HTML deliberately. Then the page as it was is saved as a restore point in the variant's version history (snapshot_version_id), and the edit is saved against the version you read. version_id is required: pass the one get_ai_builder_document returned with the document you planned the operations against. If the variant has been saved since — in the Unbounce AI builder or by another call — the edit fails as a conflict and nothing is written, because paths and replacements from your read may no longer mean what you intended. Re-read with get_ai_builder_document, re-plan against what is there now, and retry with its version_id. A successful edit returns the new version_id, so a further edit can follow without re-reading. variant is the variant letter and defaults to a. The page stays editable in the Unbounce AI builder; the Classic HTML tools do not apply to it. This does not publish the page.
get_account_plan
ChatGPTReturn an Unbounce account's plan (name, billing interval) and subscription (renewal date, price with currency). Defaults to the authenticated user's primary account; pass accountUuid (from list_accounts_and_clients) to target a specific accessible account. plan and subscription come back null when your role can't see billing details — visible only to an Account Owner or Administrator, null for Authors and Viewers. That's a normal result, not an error.
get_ai_builder_catalog
ChatGPTReturn what you need before authoring an AI Builder page: the component types you may use, and the client's resolved style guide. Call it first — the catalog is versioned and owned by the AI Builder team, so authoring against a remembered list eventually emits components that no longer exist. components is a projection — type, name, category, description — because the full catalog runs to hundreds of kilobytes and choosing a component needs none of it. Then call again with component_types for the definition (traits, defaults, nesting rules) of the ones you intend to use. That call returns component_details alone, without the list or the style guide you already have. Details are capped at a size a client can take in one result: any requested type that did not fit is named in deferred_component_types — call again with those. A component's own CSS is left out unless include_styles is true, and its runtime script and icons always are: the editor adds all three to the page itself. A ub-button or ub-form-button takes its label from data-label; give a form button no children, or its label renders twice. Unpaginated: the catalog is a small fixed set, returned whole (component_count says how many), like list_page_variants — there is no next_after to follow. style_guide carries the client's brand as CSS custom properties (variables) plus the style_guide_id to pin at page creation, and resolved_from saying whether it came from the client, the account, or Unbounce's default. Author within these variables rather than hard-coding colours and fonts — inline values survive neither a rebrand nor the theme editor in the Unbounce AI builder. An account whose plan lacks AI Builder (builders.ai_builder false in list_accounts_and_clients) is refused before anything is read, naming create_page_from_html as the route to take instead. Every account that can use AI Builder gets the full catalog and a style guide — Unbounce's default when the account has configured none. If the style-guide read fails, style_guide comes back null with style_guide_unavailable naming the HTTP status while the components are fine. You can still author without it; the page just won't match the account's brand. A 5xx there may be transient, so retrying the call is reasonable.
get_ai_builder_document
ChatGPTReturn the current editor document of an AI Builder page's variant, by page_uuid (from list_client_pages with builder: "ai_builder") and the numeric context (the owning client's id). The document is the page itself: a component tree plus its style rules, the same representation the Unbounce AI builder reads and writes. variant is the variant letter and defaults to a; list_page_variants names a page's variants. The result also carries version_id — the version the document was read from. A later write should send it back so the save is refused if someone edited the page in the meantime, rather than silently overwriting their work. It is absent when the variant has never been saved, and can be absent if the backend stops exposing it, so treat its absence as 'cannot take part in the conflict check', never as a reason to skip the read. updated_at rides alongside it, so you can say how stale a copy is rather than only whether it is stale. view defaults to components: the document without the editor's own bookkeeping — the style guide's data source, the :root rule binding its variables, the copies of each component's catalog CSS, the editor's interaction flags (removable, draggable, selectable and the like; locked is kept), data-ub-type attributes that repeat a node's type, and the drawing inside each svg node — most of a saved document and none of it authored. omitted counts what was left out. The components view is paged by section (the components directly under a page's root, such as a navbar, a hero or a footer) so a result stays a size a client can take. Sections not in this page are stubs with omitted_section: true and their type and id, never removed, so every path is the same as in the full document. total is the number of sections and sections the range returned in full; when next_after is present, pass it as after for the next sections, and stop when it is absent. To read one section, pass its index as after with first: 1. Pass view: "full" for the document exactly as stored, unpaged; it can run to hundreds of kilobytes. edit_ai_builder_page reads the whole document itself, so edits planned against either view behave the same; style-variable names are in get_ai_builder_catalog. This is the AI Builder counterpart of get_variant, which reads Classic pages and does not work here — the two builders store pages in entirely different ways.
get_layout_hints
ChatGPTAnalyze an MCP-managed variant's rendered layout into a structural skeleton — top-to-bottom sections, each with rows of element ids clustered left-to-right — for rewriting an absolutely-positioned page as responsive HTML (flex/grid). Identify the variant by page_uuid (from list_client_pages) and letter (from list_page_variants); use the rows as the flex/grid skeleton instead of translating absolute coordinates. Also returns per-element geometry, cross_section_overlays (elements whose vertical span crosses into later sections — reserve their column in every section they span, or that section's content hides behind them), and design_width (the fixed canvas width content is pinned to). It includes fidelity signals measured from the rendered page — treat these as ground truth, never re-derive or assume them: page_defaults (body text color, background, and typography — the baseline everything else deltas against), per-section background_color/background_image (with natural size, so a decorative accent reads differently from a full-bleed cover), per-element color/typography (measured on the element's dominant text node, so it describes what actually renders rather than the container's inherited defaults; both are omitted when an element paints no text of its own), plus text_colors (multi-color text), secondary_typography (a second distinct type style, e.g. heading + body copy), text_excerpt/text_length, images (exact src, natural and rendered size), links (exact href), row_stats (columns, alignment, vertical gaps), content_extent (natural content-column width), palette (every color on the page with usage counts — authored CSS should introduce no color outside it), loaded_fonts, and hidden_elements (present in the source but not rendered — preserve, don't surface). Meaningful only for variants imported from an existing Unbounce page via create_variant_from_existing_variant, which carry an absolutely-positioned tree; a variant authored as responsive HTML returns an empty structure. Pages built in the Unbounce builder aren't analyzable directly — import them first. Rendering takes a few seconds.
get_page_stats
ChatGPTReturn a page's A/B stats — page-level totals plus a per-variant breakdown of visits, visitors, conversions, and conversion_rate — by page_uuid (from list_client_pages). Also includes each variant's conversion_rate_delta and statistical confidence. Numbers are all-time cumulative since the last reset by default (see reset_page_stats); pass start_date/end_date (RFC 3339 UTC timestamps) to narrow to a period. visits sum exactly across adjacent windows, but visitors/conversions are visitor-deduplicated and attributed to the period where first counted, so a narrow window can show fewer than the events inside it; no window reaches data zeroed by a reset. conversions counts conversion-goal hits deduplicated per visitor — NOT the number of form submissions (the two totals legitimately differ). Every variant comes back in one response (no paging), and last_reset_at is the last reset's timestamp, or null if never reset. Errors if the page isn't found or accessible.
get_variant
ChatGPTFetch an MCP-managed variant's current draft content — body HTML, CSS, and JavaScript — into your workspace so you can inspect or edit it and re-submit with update_variant_from_html. Identify the variant by page_uuid (from list_client_pages) and letter (a/b/c, from list_page_variants). Returns the editable source as html_ref / css_refs upload:// references plus a scripts array (one entry per custom-JavaScript element: a ref to an editable .js file, an external src include, or verbatim tag markup) — pass them straight back to update_variant_from_html; the bytes never pass through this conversation. This is DRAFT (editor) content, which may differ from what's published if the variant has unpublished changes. Also returns the variant's conversion_goals (the configured goal set plus candidates) and its metadata (SEO title/description/keywords, robots noindex, favicon, Open Graph — settable via set_page_metadata). The source refs are streams and do NOT render individually in a browser — for a local preview, use the also-returned preview_ref: a standalone preview.html composed for viewing (fetch it with download or download_inband). preview.html is view-only; never submit it back as html_ref. Source is returned for MCP-managed variants only — for a variant hand-built in the Unbounce builder the result degrades to is_mcp_managed:false with its conversion_goals and metadata (both readable for any variant), an autoscale flag (true when the Classic page scales a fixed-width design to the viewport rather than reflowing per breakpoint), and no source refs.
list_accounts_and_clients
ChatGPTReturn the Unbounce accounts the authenticated user can access, each with its clients. Calls the Unbounce API on the caller's behalf.
list_assets
ChatGPTReturn a client's asset library — uploaded images and files — by the client's numeric context (from list_page_variants' page.context, or a clientId). Each asset carries its numeric id, uuid, name, content_type, file_size, and a servable cdn_url. Drop a cdn_url straight into the HTML/CSS passed to create_page_from_html / create_variant_from_html / update_variant_from_html to reuse that asset without uploading a duplicate (external URLs pass through untouched). Returns metadata and URLs only, never the file bytes. Listing appears to require an author role on the client — a client you can list pages for may still be denied here (HTTP 403). Paginated by after, but not the standard first/total contract: this backend returns a bare array with a fixed page size and no total count. Omit after for the first page; if the response includes next_after, pass it back as after for the next page, and keep going until next_after is absent.
list_client_domains
ChatGPTList the custom domains a client owns, by client_uuid (from list_accounts_and_clients). Returns { domains: [{ name, state, is_default, certificate_status }], total }. name is the domain string a page can be published on (see set_page_url); state is ok, moving, or move_error; is_default marks the account's default domain; certificate_status is SSL readiness (ok means the domain serves HTTPS). The list is complete (unpaginated); an empty list means the client has no domains, not an error.
list_client_pages
ChatGPTReturn a client's landing pages, by client_uuid (from list_accounts_and_clients), with optional server-side filters and sort. Each page carries its pageUuid (feed to list_page_variants), numeric pageId, name, url, state, an unpublished-changes flag, last-published timestamp, routing_strategy (smart_traffic / weighted / single — the authoritative traffic mode; omitted if unset), and routing_config naming the variant letters in play for that mode (the allocation letters for smart_traffic; champion + weights for weighted; the serving variant for single). For a page's variants and their traffic split, call list_page_variants with the pageUuid — a per-page variant count isn't returned here because the platform's own count is unreliable for pages under an A/B test. Paginated: total is the full post-filter count, and next_after (when present) is the offset to pass as after for the next page; page until it's absent. An empty list means no matching pages, not an error.
list_page_groups
ChatGPTList the page groups (named folders for organizing pages) a client uses, by client_uuid (from list_accounts_and_clients). Returns { groups: [{ group_uuid, group_id, name, page_count }], total }. Use group_uuid with manage_page_group and delete_page_group. The list is complete (unpaginated); an empty list means the client has no custom groups. The UI's "All Pages" view is not a real group and is never listed.
list_page_variants
ChatGPTReturn a page's variants (by page_uuid from list_client_pages), ordered champion, then other active variants, then discarded. Each variant carries its numeric variant_id (the id the page-editing tools take), its letter (a/b/c label), name, its stored SEO title and description (metadata is per-variant and unsynced, so divergence shows here at a glance — set with set_page_metadata, full set via get_variant), its standing (champion/challenger/discarded — the same vocabulary set_variant_standing takes, so you can round-trip without translating), the raw platform state (testing/paused/untested/discarded), traffic weight, is_champion, has_conversion_goal (whether the platform counts anything as a conversion on it — get_variant returns the goal definitions themselves), and a preview_url. The page block includes context (the owning client's numeric id the page-editing tools require) and routing_strategy (smart_traffic / weighted / single), the authoritative traffic mode: under smart_traffic the platform allocates dynamically, so a variant's weight is the stored split, NOT the live allocation — do not infer an A/B test from weights alone. routing_config names the variants in play for that mode (the letters in the Smart Traffic allocation; the champion + per-variant weights for a weighted test; the serving variant for single). routing_strategy and routing_config are both omitted when the page has no routing strategy set. preview_url opens that variant's staged rendering in the Unbounce app for a human to review (not edit) — it requires the viewer's own Unbounce session, so it is NOT a public URL and cannot be fetched or screenshotted by the agent. include_discarded defaults to true; set false to return only the live set (discarded_count is reported either way). Errors if the page isn't found/accessible, or isn't a landing page.
manage_page_group
ChatGPTCreate, rename, or change the membership of a client's page groups — named folders for organizing pages. Pick an action: create (needs client_uuid + name), rename (needs group_uuid + name), add_pages or remove_pages (needs group_uuid + page_uuids, and remove_pages leaves the pages themselves untouched). Discover ids with list_accounts_and_clients, list_page_groups, and list_client_pages. Groups are pure organization — changes take effect immediately, no publish step — and a page can belong to several groups at once (add_pages is additive, not a move); to delete a group entirely, use delete_page_group. create is a get-or-create: the backend enforces no name uniqueness, so a same-named group already existing is returned instead of duplicated (already_existed: true). add_pages/remove_pages report any skipped page_uuids the backend couldn't resolve (not found, not accessible, or not a Classic/CCP page) without failing the rest. Group names are capped at 20 characters and can't be blank. Returns { ok: true, … } on success or { ok: false, code, reason, remedy }.
manage_page_redirect
ChatGPTAttach, replace, or remove a page redirect (by page_uuid from list_client_pages) that forwards visitors from the page's own URL to another URL — the page stops serving its content, so this is how you retire or forward a published page without deleting it. action: "set" creates the redirect, or replaces an existing one; it needs redirect_url (an absolute URL) and an optional status_code (301 permanent, the default — for retiring/replacing a page for good, so search engines transfer the old URL's ranking to the target; 302 temporary — the original URL keeps its equity). action: "remove" restores the page's own content. Both apply IMMEDIATELY — no publish step — and are reversible (remove undoes set). This does NOT preserve a page's old URL after set_page_url: a redirect attaches to a page at its current URL and points away, so it can't resurrect an already-orphaned URL. Returns { ok: true, action, ... }.
publish_page
ChatGPTPublish a landing page (by page_uuid from list_client_pages), making its current draft state live. Publishing is the commit step: content and traffic/routing changes made by other tools stage as draft and only take effect once the page is (re)published. The publish is asynchronous — this tool polls briefly and reports done:true with state:"published" once the page settles, or done:false with the in-progress state and a note to re-check later. Errors if the page isn't found, isn't a landing page, or can't be published.
recipes
ChatGPTList common tasks you can accomplish with Unbounce MCP, each with an example prompt to ask for it. Takes no input. Returns { recipes: [{ title, prompts }], total, presentation } — use it to discover capabilities and how to phrase a request. Present the result to the user as a list or table of title + full prompt(s) verbatim — never summarize, paraphrase, or truncate the prompts (see the returned presentation directive).
reset_page_stats
ChatGPTZero ALL of an Unbounce page's A/B stats — every variant at once — by page_uuid (from list_client_pages). This is page-wide and IRREVERSIBLE, and it takes effect LIVE and IMMEDIATELY: there is no draft/publish step to undo it, and the visit/conversion history every variant decision rests on is gone the moment this returns. It starts a fresh measurement window. Call get_page_stats first if you want a fuller record. The response includes wiped, a best-effort snapshot of the totals just before the reset (as of the last stats aggregation, so it may omit the most recent traffic) — it is the only record you will have otherwise. Returns { ok: true, … } on success, or { ok: false, code, reason, remedy } so you can correct and retry.
set_conversion_goals
ChatGPTDeclare which parts of an MCP-managed page count as conversion goals: the form submission and/or clicks on chosen links (http(s) or tel:). Takes the COMPLETE desired set and converges every active variant onto it — the form posts as a goal (/fsg) or non-goal (/fsn, still captures the lead), chosen links are rewritten to the platform's goal-click tracking, and the page's stored goal list is updated. Goal identity is the URL: every anchor sharing a chosen URL converts, and a URL must exactly match an anchor href somewhere on the page (candidates are listed by create_page_from_html / create_variant_from_html / update_variant_from_html / get_variant results) — an unmatched URL is rejected with the real candidates echoed back. Page-scoped: all active variants get the same goal set, keeping A/B conversion rates comparable; discarded variants are left alone. Changes are STAGED in draft — publish the page (publish_page) to make them live. Link/phone goals are tracked (a click on the published page counts as a conversion) but do NOT appear in the Unbounce builder's Conversion Goals panel for MCP-authored pages — that panel lists only builder-native elements. Manage these goals here, not in the builder.
set_page_metadata
ChatGPTSet a page's SEO head fields and Open Graph (social-card) data: the <title>, meta description, robots noindex, favicon, and og:* tags. PARTIAL update: only the fields you pass change, null clears a field, omitted fields keep their value. Applies to ALL active variants by default — variants share one URL, so divergent metadata makes search snippets and social previews nondeterministic — pass letter to target one variant instead. favicon takes an upload:// reference or an existing asset id/uuid (from list_assets); open_graph.image takes an absolute URL or an upload:// reference (uploaded as a client asset; 1200×630 is the social-card convention). title/description/keywords accept Dynamic Text Replacement (<ub:dynamic>) tags — ask the user first. keywords is legacy (ignored by search engines since 2009) — usually leave it unset. Changes are STAGED in draft: a published page keeps its old metadata until publish_page. MCP-managed variants only; builder-built variants are skipped. The result echoes the applied metadata (also readable later via get_variant).
set_page_url
ChatGPTSet an Unbounce page's published URL — domain + path — by page_uuid from list_client_pages. path (required) is the new slug; domain (optional) defaults to the page's current domain, so you can change just the slug or move the page to another of the client's domains (from list_client_domains). The change applies immediately and puts the page into a urlchange state rather than staging as a normal draft edit, so you must (re)publish the page (publish_page) for it to serve at the new URL. Changing a published page's URL orphans the old one — existing links to it break. Returns { ok: true, url, domain, path, state, note } on success; errors if the page isn't found or the URL is rejected (e.g. the path is already taken on that domain).
set_traffic
ChatGPTStage how an Unbounce page splits visitors across its variants, by page_uuid from list_client_pages. Variant identifiers are letters (a/b/c) from list_page_variants. Choose a strategy: single (one variant serves all traffic — pass champion), weighted (fixed per-variant shares — pass weights, plus an optional champion), or dta (Smart Traffic dynamically allocates traffic — optionally pass variants to scope which take part). The change is STAGED in the page's draft and only goes live when the page is (re)published (publish_page). weighted (A/B testing) and dta (Smart Traffic) are paid features: if the account isn't entitled, the call is rejected up front before anything is staged — check the plan with get_account_plan, or use single to serve one variant. Returns { ok: true, … } with the staged state on success, or { ok: false, code, reason, remedy } so you can correct the inputs and retry.
set_variant_name
ChatGPTRename one of an Unbounce page's variants, by page_uuid from list_client_pages and the variant letter a/b/c from list_page_variants. A variant's name is editor metadata, not page content, so the rename applies immediately — no publish_page step is needed and visitors are unaffected. Returns { ok: true, … } with the new name on success, or { ok: false, code, reason, remedy } so you can correct the inputs and retry.
set_variant_standing
ChatGPTMove one of an Unbounce page's variants through the A/B-test lifecycle, by page_uuid from list_client_pages and the variant letter a/b/c from list_page_variants. Choose a standing: champion (make this variant the page's baseline), discarded (pull a challenger out of the test — reversible, not a delete), or challenger (bring a discarded variant back into the test). This is the same standing list_page_variants reports for each variant, so you can read a variant's current standing there and pass the target one here without translating. The change is STAGED in the page's draft and only goes live when the page is (re)published (publish_page). Returns { ok: true, … } with the staged state on success, or { ok: false, code, reason, remedy } so you can correct the inputs and retry.
submit_feedback
ChatGPTRecord feedback about the Unbounce MCP tools — a bug, praise, a feature request, confusion, or other — to the team's feedback store, without copy/paste. Use the mcp-feedback skill to drive this: it composes the record, redacts secrets, and shows you the exact content for an explicit yes before calling this tool. The server re-scrubs secrets and stamps identity, time, and build itself; only type and message travel through the conversation. Returns the stored feedback id and how many secrets were redacted.
unpublish_page
ChatGPTUnpublish an Unbounce landing page, by page_uuid from list_client_pages, taking it offline so its URL stops serving. The unpublish is asynchronous: the tool polls briefly and reports done:true with state:"unpublished" once the page settles, or done:false with the in-progress state and a note to re-check. Errors if the page isn't found, isn't a landing page, or isn't currently published.
update_variant_from_html
ChatGPTFull-replace an existing MCP-managed variant's body (page_uuid from list_client_pages, variant letter a/b/c from list_page_variants) with already-uploaded HTML/CSS/JS: pass the upload:// references from upload as html_ref (body-content HTML), optional css_refs (stylesheet), and optional scripts (custom-JavaScript elements). The HTML and CSS are a TRUE FULL REPLACE — CSS not passed on this call is REMOVED, so send every stylesheet you want the variant to keep, every time. JavaScript is different: OMIT scripts and the variant's existing custom-JavaScript elements are kept as they are (reported back as scripts_preserved) — that protects scripts added outside these tools, such as an Unbounce popup. Pass scripts to state the complete desired set and replace them; whatever it drops comes back in removed_scripts. Entries in removed_scripts and scripts_preserved are themselves scripts entries — pass one back unchanged to restore that script (a plain include appears as src, with its async/defer; anything else, including an include carrying other attributes, as verbatim tag). Pass scripts: [] to remove them all. Referenced images and fonts are rehosted as client-owned Unbounce assets and the markup rewritten to the CDN URLs (reuse an existing asset via its cdn_url from list_assets instead of uploading a duplicate — uploads are deduplicated against the client's library); external http(s) URLs are left untouched. A <form> in the new HTML is automatically wired for lead capture; it must be well-formed — one <form> with named fields and a submit button, no pageId/pageVariant fields, no onsubmit/onclick handlers — or the call fails listing every issue. A wired form shows a default "Thank you!" confirmation dialog on submit, so it's never a silent dead end; register a window.ub.hooks.afterFormSubmit callback (in a scripts entry or an inline <script> in the body HTML) to customize the post-submit experience or suppress the default dialog. Only MCP-managed variants can be updated this way — a variant hand-built in the Unbounce builder is refused (import it into an editable MCP copy first with create_variant_from_existing_variant, or author a fresh variant with create_variant_from_html); the reverse also holds, an MCP-managed variant cannot be edited in the Unbounce builder. The change is STAGED and only goes live when the page is (re)published (publish_page), so editing a live champion is safe. Head metadata tags in the HTML — <title>, <meta name="description|keywords|robots">, og:*, <link rel="icon"> — are lifted out and applied as this variant's metadata (reported under head_metadata, with a divergence warning when sibling variants exist — converge with set_page_metadata). Dynamic Text Replacement (<ub:dynamic>) is a paid feature: HTML containing it is rejected up front if the account isn't entitled (remove it, or check the plan with get_account_plan). File bytes are read server-side and never pass through this conversation.
upload
ChatGPTUpload local files (html/css/js + assets) to your server-side workspace without sending their bytes through this conversation. Returns one temporary upload URL per file; run the printed curl -T <localfile> '<upload_url>' for each (no auth header needed — auth is baked into the URL), so bytes go straight to storage, never through the model. Declare each file's exact byte size (wc -c <file>); the size is enforced on upload. Pass the returned upload_path values (upload://<handle>/<relpath>) to other tools — as html_ref for the HTML-authoring tools, or as an image/font reference inside authored HTML and bulk_edit_variants op values (rehosted to a client-owned CDN URL when applied). Each call creates a fresh upload folder, so refs are immutable snapshots — a later upload never changes what an existing ref points to. A relative reference written inside your HTML/CSS resolves only within the HTML's own folder: upload the HTML together with everything it references by relative path in ONE call, or write the full upload:// ref in the markup instead. Can't run shell/curl? Use upload_inband instead.
upload_inband
ChatGPTUpload text files (HTML/CSS/JS) to your server-side workspace by sending each file's content inline as a tool argument. Use this only if you cannot run shell/curl commands locally; if you can, prefer upload, which keeps bytes out of this conversation. Content rides through the conversation here, so keep files small and text-only — binary assets (images, fonts) are NOT supported: reference them by external http(s) URL instead, or reuse an existing asset's cdn_url from list_assets. Returns one upload://<handle>/<relpath> reference per file, usable anywhere the references from upload are — html_ref / css_refs, and a scripts entry's ref, for create_page_from_html / create_variant_from_html / update_variant_from_html. Relative paths share one upload folder, so a file can reference a sibling by relative path — but ONLY a sibling from the same call: each call (to this tool or to upload) creates a fresh folder, so refs are immutable snapshots and cross-call files must be referenced by their full upload:// ref, not a relative path.
version
ChatGPTReturn which build of the unbounce-mcp server is running: the git commit SHA (full and short) the deployed artifact was released from, and the environment (integration, production, or local).
whoami
ChatGPTReturn the authenticated Unbounce user (sub, id, email, first_name, last_name) by calling the Unbounce API on the caller's behalf.