Update a FoxForm form
foxform_update_formUpdate 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) andchatbot_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.
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'sid) | 'end_form'. Addthen.url(+ optionalopenNewTab) 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/rightare EXPRESSION strings: a literal ("10", "Ainda não"), a variable ("{{score}}", "{{minha_var}}" = the screen'svariableName), or arithmetic ("calc({{peso}}/(({{altura}}/100)*({{altura}}/100)))").Comparing an ANSWER: use
left: "{{<variableName of the deciding screen>}}"andright= the option'slabelOR itsvalue(both match).{{score}}is the running sum ofpointson 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: falsestores the rules but disables them.Screen-level conditional display uses the same group shape:
logic.display = { enabled: true, groups: [...], showAfterSeconds?: n }(thenis 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
| Name | Required | Description | Default |
|---|---|---|---|
| theme | No | ||
| title | No | ||
| format | No | 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`). | |
| form_id | Yes | Form ID | |
| sections | No | single_page only: the page groups as `[{ id }]`, in order. The 'Section N' label is derived from position; sections have no editable title. | |
| questions | No | Replaces the full screen list. Conditional logic goes in each screen's `logic` (see the tool description). | |
| description | No | ||
| chatbot_skin | No | chatbot only: chat chrome to imitate — 'instagram' (default) or 'whatsapp'. | |
| chatbot_profile | No | chatbot only: the profile shown in the chat header. | |
| thank_you_message | No |