Publish a form
publish_formPuts a form live on Formhaus Cloud: without form_id it creates a new form, with form_id it publishes a new version of that form. With form_id the new version replaces the live version of that form. save_form_draft, when listed, prepares changes to a live form without going live. Returns form_id, version, endpoint (POST URL for submissions), hosted_url, embed_snippet, iframe_snippet, react_snippet, vue_snippet, dashboard_url and warnings. Without an account key it also returns claim_url, expires_at and terms_url (publishing accepts these Terms for the user), and on the first call without any key agent_key and agent_key_notice. Use it when the user needs a working form (contact, waitlist, signup, survey, feedback) without writing a backend. A new version keeps form_id, endpoint and links. allow_another_response: true shows a "Submit another response" button on the success screen of the hosted page and embed; a new form starts with false, and a new version keeps the current value unless you send it. folder (account keys only): a folder id or name from list_forms; a new form goes into it and a new version moves the form into it.
Minimal valid definition: {"id":"waitlist","title":"Join the waitlist","submit":{"label":"Join"},"fields":[{"key":"email","type":"email","label":"Email","validation":{"required":true}}]}
Rules:
Top level needs id, title and submit {"label": "..."}.
Each field needs key, type and label; a hidden field may omit label. Use "fields" for one step, or "steps" (each with id, title and fields) for several, never both.
Types: text, email, phone, number, textarea, select, multiselect, radio, checkbox, switch, date, datetime, scale, rating, file. A form has at most 5 file fields; each file is up to 10 MB and must be PDF, PNG, JPEG, WebP, GIF, TXT, CSV, DOCX or XLSX. password, autocomplete, optionsFrom and custom types are refused.
select, multiselect and radio need "options": [{"value": "a", "label": "A"}].
scale shows numbered points from validation.min (0 or 1, default 1) to validation.max (default 5); rating shows 1 to validation.max stars (default 5). Both take 2 to 11 points and store a whole number. Up to 3 "options" whose value is a point, such as {"value": "10", "label": "Extremely likely"}, label that point. NPS is a scale from 0 to 10. Set "formatVersion": "1.3" when a form uses scale or rating.
radio and multiselect options may have "image": an https: or data:image/ URL shown with the label. Other URLs do not show. "columns" (1 to 4) sets the picture cards per row.
A section with "layout": "matrix" (the only layout value) shows its radio and multiselect fields as rows with shared columns; give every row the same options. A step or section may have no fields, as a welcome or statement screen.
"validation": {"required": true, "minLength": 2, "maxLength": 200, "min": 0, "max": 10, "pattern": "^[a-z]+$"}.
Format 1.5 ("formatVersion": "1.5") adds three features. {{key}} in a step or section title or description, or in a field label, helperText, placeholder or group itemLabel, shows the answer of an earlier field, such as "label": "Why did {{name}} join?". A multiselect option with "exclusive": true, such as "None of these", is the only answer when picked; put it last. "autocomplete" takes an HTML autofill token such as "given-name", "email" or "off". Hosted forms refuse cc-* and current-password tokens.
At most 32 KB and 100 fields.
JSON Schema: https://formhaus.dev/schema/form-definition.json
Store agent_key in .env as FORMHAUS_API_KEY, never in browser code; the MCP client must send it as Authorization: Bearer <agent_key>. Give claim_url to the user: they must open it within 7 days to keep the form. The text of the result has hosted_url, endpoint, embed_snippet and, when issued, agent_key and claim_url; every field is in structuredContent. On error, fix every listed path and call again.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| No | Only for a new form published without an account key: the user's email. Formhaus emails them claim_url (at most one email per address per hour, no form content). Ignored with a warning otherwise. | ||
| folder | No | Account keys only: a folder id or name from the folders that list_forms returns. A new form goes into that folder; with form_id the form moves into it. Empty string takes the form out of its folder. Omit to leave the form where it is. | |
| form_id | No | The 10-character form_id of an existing form to publish a new version of it. Requires an account key (fh_live_...) of the form's account or the agent key (fh_agent_...) that created the form. Omit to create a new form. | |
| definition | Yes | The Formhaus form definition: a JSON object, or a string containing that JSON. Minimal example: {"id":"waitlist","title":"Join the waitlist","submit":{"label":"Join"},"fields":[{"key":"email","type":"email","label":"Email","validation":{"required":true}}]} | |
| redirect_url | No | http(s) URL the hosted page and embed open after a successful submission instead of the success message. Empty string removes it. Ignored with a warning for unclaimed forms. | |
| success_message | No | Text shown after a successful submission on the hosted page and embed. Up to 500 characters. | |
| allow_another_response | No | true shows a Submit another response button on the success screen of the hosted page and embed. false hides it. A new form starts with false; a new version keeps the current value when omitted. No effect while redirect_url is set. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| form_id | Yes | ||
| version | Yes | ||
| endpoint | Yes | ||
| warnings | Yes | ||
| agent_key | No | ||
| claim_url | No | ||
| terms_url | No | ||
| expires_at | No | ||
| hosted_url | Yes | ||
| vue_snippet | Yes | ||
| form_version | Yes | ||
| dashboard_url | Yes | ||
| embed_snippet | Yes | ||
| react_snippet | Yes | ||
| iframe_snippet | Yes | ||
| agent_key_notice | No |