Skip to main content
Glama

Update a FoxForm form

foxform_update_form
Destructive

Update an existing form's fields, including each screen's conditional logic (branching). Requires a WRITE-scoped API key. Only the fields you pass are changed.

Args:

  • form_id (string): the form to update (required)

  • title (string, optional)

  • description (string, optional)

  • theme (string, optional): one of amber|base|midnight|ocean|sunset|forest|lavender|minimal

  • questions (array, optional): replaces the FULL screen list — there is no per-screen patch. To add logic to one screen: call foxform_get_form with response_format 'json', edit that screen's logic, and send the whole array back.

  • thank_you_message (string, optional)

  • format ('single_question' | 'single_page' | 'chatbot', optional)

  • sections (array, optional): single_page page groups, [{ id }]

  • chatbot_skin ('instagram' | 'whatsapp', optional)

  • chatbot_profile (object, optional): chatbot header profile ({ name, handle, followers_text, phone, status, verified, avatar_url })

Returns: { form } with the updated form.

Screen fields are validated: unknown fields are REJECTED instead of being stored and ignored (the API accepts arbitrary keys but no renderer reads them), then.targetScreenId must be the id of a screen in the same payload, and {{variables}} that no screen exposes come back as warnings.

Form format (DEVF-296 / DEVF-322): pass format to choose how the form is presented.

  • 'single_question' (default): one screen at a time (classic).

  • 'single_page': every screen on one scrollable page. Optionally group screens into pages with sections: [{ id }] in order (the 'Section N' label comes from position; no editable title).

  • 'chatbot': an Instagram/WhatsApp DM-style conversation. Set chatbot_skin ('instagram' | 'whatsapp', default instagram) and chatbot_profile (all fields optional, each with a fallback). The question/branching engine is unchanged — only the presentation differs. These fields require the account's form-format feature to be enabled (live in production); when off the API ignores them and the form stays single_question.

End screen ('end'): title + description, an optional button (buttonEnabled false hides it; buttonText, buttonLink, buttonOpenNewTab) and an optional automatic redirect after submit: autoRedirect true + autoRedirectUrl (absolute http/https URL; the form link's query params such as utm_* are passed along and {{param}} tokens are filled from them) + autoRedirectDelay (seconds 0-10, default 3). The response is recorded before the redirect; respondents see a "Redirecting in N s" line with a "Go now" link.

CONDITIONAL LOGIC (branching), per screen — stored in questions[].logic:

logic.conditionalNavigationV2 = { enabled: true, groups: [ // groups are OR-joined; FIRST matching group wins { id: "grp-1", conditions: [ // conditions inside a group are AND-joined { id: "cond-1", left: "{{quer_testar}}", operator: "equal_to", right: "Ainda não" } ], then: { type: "specific_screen", targetScreenId: "s-motivos" } } ] }

  • then.type: 'next_screen' | 'previous_screen' | 'specific_screen' (needs targetScreenId = another screen's id) | 'end_form'. Add then.url (+ optional openNewTab) to redirect to an external URL instead.

  • operator: 'equal_to' | 'not_equal_to' | 'greater_than' | 'greater_or_equal_than' | 'less_than' | 'less_or_equal_than' | 'contains'.

  • left/right are EXPRESSION strings: a literal ("10", "Ainda não"), a variable ("{{score}}", "{{minha_var}}" = the screen's variableName), or arithmetic ("calc({{peso}}/(({{altura}}/100)*({{altura}}/100)))").

  • Comparing an ANSWER: use left: "{{<variableName of the deciding screen>}}" and right = the option's label OR its value (both match).

  • {{score}} is the running sum of points on the options picked so far (choices[].points, images[].points) — that is how score-based branching works.

  • A navigation group with no conditions NEVER matches. enabled: false stores the rules but disables them.

  • Screen-level conditional display uses the same group shape: logic.display = { enabled: true, groups: [...], showAfterSeconds?: n } (then is ignored — THEN means "show").

  • Other logic keys: logic.autoAdvance = { enabled, delaySeconds? }, logic.navigationBehavior = { onButtonClick?, onAutoAdvance?, targetScreenId? }.

  • logic.conditionalNavigation (legacy, pre-DEVF-161) is still read and migrated on load — don't author new rules with it.

Unknown fields are REJECTED (they used to be stored and silently ignored): logic as an array, or rules/branching/conditions/goto/jump/nextScreen anywhere, are not read by any renderer.

Input Schema

NameRequiredDescriptionDefault
themeNo
titleNo
formatNoForm format: 'single_question' (default — one screen at a time), 'single_page' (all screens on one scrollable page, grouped by `sections`), or 'chatbot' (an Instagram/WhatsApp DM-style conversation; set `chatbot_skin` + `chatbot_profile`).
form_idYesForm ID
sectionsNosingle_page only: the page groups as `[{ id }]`, in order. The 'Section N' label is derived from position; sections have no editable title.
questionsNoReplaces the full screen list. Conditional logic goes in each screen's `logic` (see the tool description).
descriptionNo
chatbot_skinNochatbot only: chat chrome to imitate — 'instagram' (default) or 'whatsapp'.
chatbot_profileNochatbot only: the profile shown in the chat header.
thank_you_messageNo

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed4 schema fields changed
    • addedInput schema / properties / chatbot_profile
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "chatbot only: the profile shown in the chat header.",
      +  "properties": {
      +    "avatar_url": {
      +      "description": "Avatar image URL; falls back to the name's initials.",
      +      "type": "string"
      +    },
      +    "followers_text": {
      +      "description": "Free text under the name, e.g. followers/posts (Instagram only).",
      +      "type": "string"
      +    },
      +    "handle": {
      +      "description": "Instagram @handle (Instagram skin only).",
      +      "type": "string"
      +    },
      +    "name": {
      +      "description": "Display name in the chat header (both skins; default 'Sua Marca').",
      +      "type": "string"
      +    },
      +    "phone": {
      +      "description": "Shown phone number (WhatsApp skin only).",
      +      "type": "string"
      +    },
      +    "status": {
      +      "description": "Header status line (both skins; empty on WhatsApp shows the phone).",
      +      "type": "string"
      +    },
      +    "verified": {
      +      "description": "Blue verified badge (Instagram only; default true).",
      +      "type": "boolean"
      +    }
      +  },
      +  "type": "object"
      +}
    • addedInput schema / properties / chatbot_skin
      Added value: +{
      +  "description": "chatbot only: chat chrome to imitate — 'instagram' (default) or 'whatsapp'.",
      +  "enum": [
      +    "instagram",
      +    "whatsapp"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / format
      Added value: +{
      +  "description": "Form format: 'single_question' (default — one screen at a time), 'single_page' (all screens on one scrollable page, grouped by `sections`), or 'chatbot' (an Instagram/WhatsApp DM-style conversation; set `chatbot_skin` + `chatbot_profile`).",
      +  "enum": [
      +    "single_question",
      +    "single_page",
      +    "chatbot"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / sections
      Added value: +{
      +  "description": "single_page only: the page groups as `[{ id }]`, in order. The 'Section N' label is derived from position; sections have no editable title.",
      +  "items": {
      +    "additionalProperties": false,
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id"
      +    ],
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
  2. First observed

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the destructive/open-world annotations, the description discloses important mutation behavior: questions replaces the full screen list, there is no per-screen patch, unknown fields are rejected rather than ignored, conditional target screen IDs are validated, and feature-flagged format fields may be silently ignored. It also explains the redirect behavior and validation warnings, providing rich context for a destructive update tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but mostly justified by the complexity of conditional-logic authoring, and it is well front-loaded with purpose, usage, and argument semantics. Some repetition exists, notably the unknown-fields rejection warning appearing twice, which keeps it from being maximally concise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 10 parameters, nested conditional-logic objects, no output schema, and destructive annotations, the description is exceptionally complete. It covers parameters, validation rules, full replacement caveats, format presentation behavior, end-screen redirects, and conditional branching syntax, leaving little ambiguity for an agent to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Despite only 60% schema description coverage, the description documents every parameter's meaning and constraints, including theme enum values, format-specific fields, sections behavior, chatbot_skin/chatbot_profile details, and the full replacement semantics of questions. It adds substantial meaning beyond the input schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: "Update an existing form's fields, including each screen's conditional logic (branching)." That clearly distinguishes it from sibling tools such as foxform_create_form and foxform_get_form, and the scope is immediately understandable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives concrete usage context, including the WRITE-scoped API key requirement and the crucial distinction that only passed fields change. It also names an alternative workflow for editing one screen's logic: call foxform_get_form with response_format 'json', edit that screen, then resend the full questions array. It does not broadly contrast with create/publish/unpublish, but the update-specific guidance is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources