FormHug
Create forms, surveys, quizzes
- Category
- Productivity
- Primary Subcategory
- Calendar & Scheduling Apps
Integration details
Description
Create forms for registrations, surveys, quizzes, assessments, and bookings inside ChatGPT. Describe what you need in natural language, and FormHug generates a complete form with fields, descriptions, logic, and layout. You can refine forms through conversation, customize themes, publish forms, collect submissions, and analyze responses — all in the same chat. FormHug helps turn everyday data collection into a simple conversational workflow.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Primary Subcategory
- Calendar & Scheduling Apps
- Secondary Subcategories
- None listed
- Brand
- FormHug
- Access
- Account required
- First tracked
- 2026-06-17
- Tool count
- 31
- Geography
- US
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
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 Form & Survey Builders
View Category31 tools agents can invoke
Add one or more fields to an existing form. By default they are appended to the end in order; give a field a `before` / `after` anchor to place it elsewhere (see Positioning below). Each field is an object with a snake_case `type` and a `label`. Use `type` from this exact list: - Basic: `short_text`, `long_text`, `radio`, `checkbox`, `dropdown`, `number`, `email`, `phone`, `date`, `name`, `url`, `address`, `rating`, `nps`, `attachment`, `signature`, `ranking`, `time`, `location` - Advanced: `cascade`, `matrix`, `matrix_rating`, `likert`, `table`, `product`, `booking`, `linked_form` (read `formhug://field-types/<name>` for the per-type shape) - Display-only (collect no input): `page_break` (paginate a `classic`-layout form), `description` (a text/heading block). `label` is optional on these. See `formhug://field-types` for keys and `page_break` placement rules. Choice fields (`radio`, `checkbox`, `dropdown`) take `choices` as an **array of objects**, one per option. Each choice has `label` (display text) plus at most one free-text role: - `is_other: true` — marks this choice as the "other / please specify" catch-all. The label is shown as-is and the respondent fills in their own value (at most one per field). - `allow_extended_text: true` — keeps the choice's fixed label and lets the respondent add a note alongside it. May also set `extended_text_required` and `extended_text_placeholder`. Applies to `radio` and `checkbox` only; `dropdown` ignores these keys. Set just one of those roles per choice — they capture free text in different ways. Plain (no role) choices accept neither. Optional on any choice: `selected: true` to pre-select it. Example: ```json {"type": "radio", "label": "How did you hear about us?", "choices": [ {"label": "Friend"}, {"label": "Social media", "allow_extended_text": true, "extended_text_placeholder": "Which platform?"}, {"label": "Other", "is_other": true} ]} ``` For type-specific keys on the other field types (e.g. `precision` and `range_min`/`range_max` on `number`, `rating_max` on `rating`, `precision` on `date`), read `formhug://field-types/basic`. Submitted entry value shapes for each type are in `formhug://entry-values`. `api_code` is server-assigned on create; supply an existing `api_code` only when you want to target that field (or nested choice / statement / dimension / sub_choice) for update via `update_fields`. Positioning: each field object may carry ONE of `before` / `after`, whose value is the api_code of an **existing** field — the new field is inserted immediately before / after that anchor. Fields with no anchor are appended in order. Anchors can only reference existing fields (new fields have no api_code yet). All placements are applied in a single update, so the saved result is atomically valid. Pagination (`page_break`): a paginated form needs **at least 2** `page_break` fields, the **first** field must be a page_break, and each page starts with one. So to paginate an existing single-page form, add the breaks **with anchors in one call** — e.g. `{"type": "page_break", "before": "<api_code of the current first field>"}` plus `{"type": "page_break", "before": "<api_code of the field that should start page 2>"}`. Appending page_breaks to the end, or adding just one, is rejected by the placement rules. (page_break only paginates `classic`-layout forms.) For Quiz/Assessment forms, new fields may include `answers` and (Quiz only) `answer_explanation` — same shape as `create_form`. After appending scorable fields, ASK THE USER whether the form's **score-based feedbacks** (post-submission score-range comments configured via `update_form_settings` → `after_submission.report.score_based_feedbacks`) need to be added or adjusted, since the total possible score has changed. This tool re-sends the form's existing fields with their api_codes so they are preserved.
add_fields
Create a new folder. By default the folder is created at the top level (desktop). Pass parent_folder_token to nest it under another folder.
create_folder
Create a new form. Each field is an object with a snake_case `type` and a `label`. Use `type` from this exact list: - Basic: `short_text`, `long_text`, `radio`, `checkbox`, `dropdown`, `number`, `email`, `phone`, `date`, `name`, `url`, `address`, `rating`, `nps`, `attachment`, `signature`, `ranking`, `time`, `location` - Advanced: `cascade`, `matrix`, `matrix_rating`, `likert`, `table`, `product`, `booking`, `linked_form` (read `formhug://field-types/<name>` for the per-type shape) - Display-only (collect no input): `page_break` (paginate a `classic`-layout form), `description` (a text/heading block). `label` is optional on these. See `formhug://field-types` for keys and `page_break` placement rules. Choice fields (`radio`, `checkbox`, `dropdown`) take `choices` as an **array of objects**, one per option. Each choice has `label` (display text) plus at most one free-text role: - `is_other: true` — marks this choice as the "other / please specify" catch-all. The label is shown as-is and the respondent fills in their own value (at most one per field). - `allow_extended_text: true` — keeps the choice's fixed label and lets the respondent add a note alongside it. May also set `extended_text_required` and `extended_text_placeholder`. Applies to `radio` and `checkbox` only; `dropdown` ignores these keys. Set just one of those roles per choice — they capture free text in different ways. Plain (no role) choices accept neither. Optional on any choice: `selected: true` to pre-select it. Example: ```json {"type": "radio", "label": "How did you hear about us?", "choices": [ {"label": "Friend"}, {"label": "Social media", "allow_extended_text": true, "extended_text_placeholder": "Which platform?"}, {"label": "Other", "is_other": true} ]} ``` For type-specific keys on the other field types (e.g. `precision` and `range_min`/`range_max` on `number`, `rating_max` on `rating`, `precision` on `date`), read `formhug://field-types/basic`. Submitted entry value shapes for each type are in `formhug://entry-values`. `api_code` is server-assigned on create; supply an existing `api_code` only when you want to target that field (or nested choice / statement / dimension / sub_choice) for update via `update_fields`. Header image (required): pass search keywords as `header_image` (e.g. "soccer field", "office workspace") and the server picks a suitable image. Layout (optional): choose how questions are presented. - `classic` — many questions on one scrollable page. Insert `page_break` fields to paginate it; each page can still hold multiple questions. Best for short, data-entry forms people fill all at once (registration, sign-up, contact, order, application, lead capture). - `card` — one question per screen, advanced one at a time (a focused, conversational flow; `page_break` has no effect). Best for engagement-oriented, multi-question forms. When omitted, the form is created as `classic`. Only use `card` when the user explicitly asks for a one-question-at-a-time / conversational flow — do not infer it from the form's purpose. Switching layout recomputes the theme palette. After the form is created, briefly walk the user through what it contains — list the fields it includes (label and type, and whether each is required) so they can see the structure at a glance. Quiz / Assessment scenes: - Quiz (`scene: "quiz"`): an exam with correct answers. Each scorable field has one right answer with a point value (`answers`) and an optional `answer_explanation`. Scorable types: `radio`, `checkbox`, `dropdown`, `short_text`, `long_text`, `number`. - Assessment (`scene: "assessment"`): an evaluation where every choice carries a score. Scorable types: `radio`, `checkbox`, `dropdown`. - Omit `scene` for a regular form. Per-field scoring keys (Quiz/Assessment only): - `answers`: Quiz — single element `[{"answer": <value>, "score": <points>}]`. For choice fields, `answer` is a 0-based index array (e.g. `[0]` or `[0, 2]`). For `short_text`/`long_text`/`number`, `answer` is the expected value. Assessment — array with length equal to `choices`, each `{"score": <points>, "answer": [<index>]}` mapping to the corresponding choice. - `answer_explanation`: Quiz only — string explaining the correct answer. After creating a quiz or assessment form, ASK THE USER whether they want to set up **score-based feedbacks** — comments shown to respondents on the result page based on which score range their total falls into (e.g. 0–60 "Needs review", 60–80 "Passing", 80–100 "Excellent"). If they do, compute the score ranges from the sum of per-field `score` values you just configured, then call `update_form_settings` with `after_submission.mode: "report"` and `after_submission.report.score_based_feedbacks: [{ start_point, end_point, comment }, ...]`.
create_form
Create a webhook integration on a form. The webhook delivers a JSON payload to `url` whenever any of the subscribed `trigger_events` fires. Use `get_webhook_sample_payload` to preview the payload shape. Requires the `integration:write` scope.
create_webhook
Delete a folder. Forms and sub-folders inside it are moved to the desktop (top level).
delete_folder
Permanently delete a form and all its entries. This cannot be undone.
delete_form
Permanently delete a webhook integration. This cannot be undone. Requires the `integration:write` scope.
delete_webhook
Get info about the currently authenticated user: email, display name, avatar, account creation date, total created forms / entries, and billing account details (organization name and current subscription plan code/name/period_end/cancel_at). Useful for confirming who is signed in or checking the current plan.
get_me
Get a single entry by its serial number.
get_entry
Get form detail including all field definitions (labels, types, choices). Use this to discover field api_codes before submitting entries or to understand the form structure.
get_form
Get a form's settings: locale, timezone, after-submission behavior (show_message / redirect / report), availability (manual close, schedule window, entries cap, countdown), access control (password), and submission options (progress bar, field numbering). Use this before update_form_settings to see what's currently set.
get_form_settings
Get the current visual theme of a form: header image(s), wallpaper (color, or a card-layout background image), primary color, submit button style, typography (form_header / field_label / choice_style), general button, and form_container styling. Use this before update_form_theme to see what's currently set. Read formhug://themes for design guidance.
get_form_theme
Get the structure of any published form (fields, api_codes, types, choices). Use this to discover field api_codes before calling submit_entry on a form you don't own. For password-protected forms, pass the password to unlock the structure.
get_published_form
Get an example payload that the webhook would deliver for the given trigger_event. Useful for testing the receiver side. The payload has `event_type` plus a `data` object containing the form token/name and an `entry` keyed by each field's api_code with example values, alongside meta fields (serial_number, created_at, info_*, etc.). Requires the `form:read` scope.
get_webhook_sample_payload
List entries (submissions) for a form. Use get_form first to see field api_codes. Results are paginated. Use the next_cursor from the response to fetch the next page.
list_entries
List all folders owned by the current user (not paginated). Returns `{records}` where each folder includes parent_folder_token (null for top-level), child_folder_count, child_form_count, created_at, and ancestors — a list of {token, name} from the root down to the immediate parent (empty for top-level folders). Use the folder token when creating or moving forms.
list_folders
List forms owned by the current user. Optionally filter by name or folder. Results are paginated; pass next_cursor from the response back as cursor to fetch the next page.
list_forms
List webhook integrations configured on a form. Returns `{records}`; each webhook entry includes its id, delivery url, subscribed trigger_events (entry_created/entry_updated/entry_paid), enabled flag, and timestamps. Requires the `integration:read` scope on the access token.
list_webhooks
Reorder a field within a form by placing it immediately before or after another field. Pass exactly one of `before` or `after` — the anchor field's `api_code`. The moved field's `api_code` cannot be the same as the anchor. For bulk reorders, call this tool once per move. Use `get_form` or `preview_form` to discover api_codes and the current order.
move_field
Move a form into a specific folder. If the form is already in a folder, you must provide the original_folder_token. Use get_form to check whether the form already belongs to a folder (look for folder.token in the response), and use list_folders to find available folder tokens.
move_form_to_folder
Prepare a 2-step upload for an `attachment` field, returning a one-time `upload_id` and an `upload_url`. Use this so `submit_entry` can include file uploads (otherwise `attachment` fields must be skipped). This MCP cannot transfer file bytes — YOU (the client) upload the file directly: 1. Discover the form's field api_codes (`get_form` / `get_published_form`). 2. Call `prepare_entry_attachment_upload` with `form_token`, the attachment field's `field_api_code`, `file_name`, `content_type`, and `file_size` (bytes). For a file column inside a `table` / `matrix` field, also pass `dimension_api_code`. 3. `PUT` the raw file bytes to the returned `upload_url`, setting `Content-Type` to exactly the declared `content_type`. Use the URL exactly as returned. 4. Submit the `upload_id` under that field in `submit_entry`'s `field_values` — an array of upload_ids (a bare string is also accepted): - Plain attachment field: `{ "<field_api_code>": ["<upload_id>"] }` - Table column: `{ "<table_api_code>": [ { "<dimension_api_code>": ["<upload_id>"] } ] }` Call once per file; collect several upload_ids into the array for a multi-file field. The credential expires (see `expires_at`), so PUT and submit promptly. Constraints: `content_type` must satisfy the field's `media_type` config (never text/html, text/css, text/javascript, application/x-httpd-php); `file_size` ≤ the field's `max_size`.
prepare_entry_attachment_upload
Prepare a 2-step upload for a form header or wallpaper background image, returning a one-time `upload_id` and an `upload_url`. This MCP cannot transfer file bytes — YOU (the client) upload the file directly: 1. Call `prepare_image_upload` with the image's `purpose`, `file_name`, `content_type` (image/jpeg | image/png | image/webp | image/gif), and `file_size` (bytes, ≤ 5 MB). 2. `PUT` the raw file bytes to the returned `upload_url`, setting the `Content-Type` header to exactly the `content_type` you declared. Use the URL exactly as returned — e.g. `curl -X PUT --data-binary @cover.png -H "Content-Type: image/png" "<upload_url>"`. 3. Reference the `upload_id` in `update_form_theme`: - `header_image` → `{ "header": { "images": [{ "upload_id": "<upload_id>" }] } }` - `wallpaper_image` → `{ "wallpaper": { "image": { "upload_id": "<upload_id>" } } }` (only on `card`-layout forms). The theme update registers the attachment and re-derives the palette. The credential expires (see `expires_at`), so PUT and apply it promptly. For header images you can skip uploads entirely by passing `header.image_keywords` to `update_form_theme` (the server auto-picks an Unsplash photo).
prepare_image_upload
Get a human-readable markdown preview of a form's structure. Shows the form title, description, shareable URL, and all fields with their types, requirements, and choices. Useful for quickly reviewing a form's layout.
preview_form
Remove a field from a form by its api_code. Use get_form or preview_form first to find the api_code of the field to remove.
remove_field
Submit a new entry. Works for both your own forms and any published form. This tool first tries the public-entry endpoint; if the form is not found that way (404/403), it falls back to the owner endpoint. Before submitting, discover field api_codes and types: - Your own forms: `get_form` (also exposes field attributes) - Any published form: `get_published_form` `field_values` is a JSON object keyed by each field's api_code. The value shape depends on the field's snake_case type. Common types: - `short_text` / `long_text` / `email` / `url` / `name`: string - `phone`: object `{country_id, country_code?, number}` — e.g. `{"country_id": "US", "number": "6267807789"}` - `number`: number - `radio` / `dropdown`: object `{value, other_text?, extended_text?}` where `value` is the choice's api_code (dropdown does not support `extended_text`). A bare string is accepted as shorthand for `{value: <string>}`. Pass `null` to clear. - `checkbox`: array of `{value, other_text?, extended_text?}` (bare strings accepted as shorthand). Empty array clears the selection. - `rating`: integer 1..rating_max - `nps`: integer 0..10 - `date`: naive wall-clock string formatted per the field's `precision` (`"YYYY-MM"`, `"YYYY-MM-DD"`, `"YYYY-MM-DD HH:mm"`, `"YYYY-MM-DD HH:mm:ss"`); interpreted in the form's `timezone` (defaults to UTC) - `address`: `{address_line1, address_line2, city, state, postal_code, country}` - `time`: `{hour, minute, second?}` (string values) - `location`: `{longitude, latitude, address}` - `attachment`: array of `upload_id` strings from `prepare_entry_attachment_upload` (prepare it, then PUT the file to the returned upload_url). A bare string is accepted; omit / empty array when no file. For nested/complex types (`cascade`, `matrix`, `matrix_rating`, `likert`, `ranking`, `table`, `product`, `booking`, `linked_form`) and a full reference, read the resource `formhug://entry-values`. `signature` is drawn in the browser and cannot be submitted via this MCP — skip it and inform the user. Unknown api_codes are dropped silently by the server.
submit_entry
Update one or more existing fields on a form, identified by `api_code`. Call `get_form` first to discover the current `api_code`, `type`, and existing nested shapes (choices, statements, dimensions). Each update entry contains the field's existing `api_code` plus any keys you want to change (label, required, placeholder, choices, statements, dimensions, …). A field's `type` is fixed at creation and stays as-is. Merge semantics: keys you supply replace the matching keys on the existing field; keys you omit are preserved. Merging is **shallow** — passing `choices` replaces the entire choices array, so include every choice you want to keep. For each choice: - Keep it unchanged: include the choice with its existing `api_code`. - Update it: include the existing `api_code` plus the keys to change. - Add it: include the choice with `api_code` omitted (server assigns one). - Delete it: leave it out of the array. `choices` is always an array of objects shaped `{label, api_code?, is_other?, allow_extended_text?, extended_text_required?, extended_text_placeholder?, selected?}` (`dropdown` ignores the `*extended_text*` keys). Example replacing the choices on a `radio` field: ```json {"api_code": "field_2", "choices": [ {"api_code": "choice_abc", "label": "Female"}, {"api_code": "choice_def", "label": "Male"}, {"label": "Prefer not to say"} ]} ``` The same shallow-replace rule with the same add/update/delete semantics applies to `statements`, `dimensions`, `sub_choices`, `goods_items`, and `reservation_items`. Fields not mentioned in `updates` are left untouched. To add new fields use `add_fields`; to delete a field use `remove_field`. For Quiz/Assessment forms: if your update changed any scorable field's `answers`/`score` values or its choice set, ASK THE USER whether the form's **score-based feedbacks** (post-submission score-range comments configured via `update_form_settings` → `after_submission.report.score_based_feedbacks`) need to be updated, since the total possible score or the meaningful thresholds may have shifted.
update_fields
Rename a folder. Only the folder name can be changed; folders cannot be moved into other folders via this tool.
update_folder
Update a form's name, description, locale, timezone, or layout, and/or move it between folders. To move the form, pass folder_token (and original_folder_token if the form currently belongs to a folder); pass an empty folder_token to move the form back to the desktop. Folder fields can be combined with attribute updates in a single call. Theme changes are not done here — use update_form_theme.
update_form
Update a form's settings (locale, timezone, after-submission behavior, availability, access control, submission options) using partial-merge semantics. Top-level groups (and individual fields within each group) you omit are left unchanged; explicit `null` clears the corresponding feature. After-submission `mode` selects which branch is authoritative: - `show_message` (default for regular forms): set `after_submission.show_message`. Pick `style: "rich_text"` and supply HTML in `rich_text`, OR `style: "text"` and supply `text.title` / `text.description`. - `redirect`: set `after_submission.redirect.url`. Optionally append field values via `redirect.fields` (array of field api_codes). - `report`: quiz/assessment forms only. Set `after_submission.report.score_based_feedbacks` to a non-empty array of `{ start_point, end_point, comment }` ranges. Each entry defines a score interval (inclusive on both ends) and the HTML `comment` shown to the respondent after submission when their total falls in that interval — use this to give tiered feedback (e.g. low score → "review these topics", high score → "great work"). Ranges should cover the full 0..max-possible-score span without gaps; `comment` accepts HTML. Availability: - `manually_closed: true` closes the form regardless of schedule. - `schedule: { start_at, end_at }` (ISO 8601 date-times) defines an availability window. Pass `schedule: null` to clear. - `entries_limit` caps total submissions (positive integer, or null to remove the cap). - `show_countdown` / `show_form_before_open` toggle respondent-facing hints around the schedule. Access control: `access_password` gates the form behind a shared password (null to remove). Submission options: `show_progress` (progress bar) and `show_field_number` (numbered fields) are independent toggles.
update_form_settings
Update a form's visual theme using partial-merge semantics — keys you include are written, keys you omit are left unchanged, and explicit `null` clears a value. Read `formhug://themes` first for design guidance and the full key catalog (allowed font sizes, weights, border widths, layout constraints). Header image — set it one of two ways: `header.image_keywords` (Unsplash search; the server auto-picks an image) **or** `header.images` as an array whose items each carry exactly one of `upload_id` (a fresh image from `prepare_image_upload`, purpose `header_image`) or `id` (reuse an image already on the form). Pass `header.images: []` to clear all header images. Changing the header image triggers automatic palette derivation: the server overwrites wallpaper / submit button / typography colors etc. derived from the new image, unless the same PUT explicitly sets that key (then the user's value wins). Wallpaper background image (`card` layout only) — set `wallpaper.image` to an object with exactly one of `upload_id` (from `prepare_image_upload`, purpose `wallpaper_image`) or `id` (reuse an existing one); `null` clears it. `primary_color` is an alias for `submit_button.style.background_color` — writing either updates the same field. Sending conflicting values for both in one request is rejected with 422. `wallpaper.color` and `wallpaper.image` are mutually exclusive (setting one clears the other); `wallpaper.image` (a background image) is only accepted when the form's layout is `card`, while `form_container.style` is only accepted when the layout is `classic`.
update_form_theme
Update a webhook integration's url, subscribed trigger_events, or enabled flag. Only the fields you pass are changed; omit a field to leave it untouched. Use `list_webhooks` to find the webhook id. Requires the `integration:write` scope.
update_webhook
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 FormHug alternatives on ChatGPT?
As of 2026-09-13, FormHug competes with CalcForms, Clipform, Cognito MCP, CrowdHum, Dayalogs, Formbyte, FormEdge, Formester, Formfacade, Forms AI, Formspry, Fyrebox, Jotform, Letsfind, Neartail, PlatoForms, SurveyMonkey, SurveySparrow: Forms & Surveys, Tally, Typeform, Walla in ChatGPT Form & Survey Builders, 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.