Forms API

Build and publish forms, read responses and submit as a signed-in caller.

Forms collect data from people: a contact form, a job application, an order enquiry. This API builds forms, publishes them, reads the responses, and submits on behalf of a signed-in caller. The public side of a published form, which anyone on the internet can use, is described in Public endpoints.

If the Forms app is switched off in Settings › Apps, these routes answer 404 NOT_FOUND.

Endpoints

Permissions are checked against the form (its key is the resource), so a role can be limited to particular forms.

Method Path Permission What it does
GET /orgs/{orgId}/forms forms:read List forms you can see
POST /orgs/{orgId}/forms forms:manage Create a form (as a draft)
GET /orgs/{orgId}/forms/{formId} forms:read Get a form
PATCH /orgs/{orgId}/forms/{formId} forms:manage Replace a form's definition
DELETE /orgs/{orgId}/forms/{formId} forms:manage Delete a form and its responses (answers 200 with {"deleted": true})
POST /orgs/{orgId}/forms/{formId}/publish forms:manage Make it live
POST /orgs/{orgId}/forms/{formId}/unpublish forms:manage Take it back to draft
GET /orgs/{orgId}/forms/{formId}/submissions forms:responses Read responses
POST /orgs/{orgId}/forms/{formId}/submit forms:submit Submit as the caller
POST /orgs/{orgId}/forms/generate forms:manage Draft fields from a description

GET /forms answers { "forms": [ … ] }.

Reading what others submitted (forms:responses) is deliberately separate from being allowed to fill in a form (forms:submit).

The form object

{
  "id": "0190f400-…",
  "key": "contact-us",
  "name": "Contact us",
  "description": "Ask us anything",
  "fields": [
    { "key": "name", "label": "Your name", "type": "string", "required": true },
    { "key": "email", "label": "Email", "type": "email", "required": true },
    { "key": "topic", "label": "Topic", "type": "select", "rules": { "options": ["Sales", "Support"] } },
    { "key": "message", "label": "Message", "type": "text", "help": "Tell us a little more", "placeholder": "…" }
  ],
  "object_type": "leads",
  "function_id": "",
  "status": "draft",
  "settings": { "submit_label": "Send", "success_message": "Thanks, we will be in touch.", "redirect_url": "" },
  "created_at": "2026-10-01T08:00:00Z",
  "updated_at": "2026-10-01T08:00:00Z"
}

POST /orgs/{orgId}/forms

PATCH takes the same body and replaces the whole definition, so send everything you want to keep.

Field Type Required Notes
name string Yes Up to 200 characters
key string No The form's address segment: lowercase letters, digits and dashes. Made from the name when omitted. Unique in the workspace
description string No
fields array Yes 1 to 60 fields. See below
object_type string No A record type key. Each submission also creates a record of that type. You need records:create on that type to save or publish the form
function_id string No A function that is handed each submission
settings.submit_label string No Button text
settings.success_message string No Shown after submitting
settings.redirect_url string No Where to send the browser after submitting. Must be an absolute http or https address

Fields

Key Notes
key Starts with a lowercase letter; lowercase letters, digits, underscore. Unique in the form. Leading underscores are reserved
label Shown to the person filling in. Defaults to the key
type string, text, number, bool, date, datetime, currency, select, multiselect, email or url. References and files are not allowed on forms
required Boolean
help, placeholder Optional text
rules The same rules as record fields (computed formula fields are refused): options (required for select and multiselect), min, max, min_len, max_len, pattern, unit, default. See Record types and fields

Submitted values are checked with the same validator as record fields, so a form can never accept something a record would refuse. If object_type is set, use that type's own field keys.

New forms are created as draft. A draft is invisible to the public.

Errors: 400 VALIDATION_ERROR (the message names the problem), 403 FORBIDDEN, 409 KEY_TAKEN.

Publish and unpublish

Both answer 200 with the form. Publishing does not by itself open the form to the world: a published form is reachable by visitors who are not signed in only when the workspace's public-visitor role grants forms:read and forms:submit on it. See Public endpoints.

GET /orgs/{orgId}/forms/{formId}/submissions

Cursor paging, newest first.

Parameter Notes
limit Maximum 100
cursor The next_cursor of the previous page
{
  "data": {
    "submissions": [
      {
        "id": "0190f410-…",
        "values": { "name": "Asha", "email": "asha@example.com", "message": "Hello" },
        "record_id": "0190f411-…",
        "status": "processed",
        "created_at": "2026-10-04T09:30:00Z"
      }
    ],
    "total": 58,
    "next_cursor": "0190f410-…"
  }
}

status is received, processed or failed. A submission is always saved first. If creating the record or running the function then fails, the submission stays, is marked failed with an error, and the submitter still sees success. submitted_by is present for signed-in submitters.

POST /orgs/{orgId}/forms/{formId}/submit

Submit as the caller, with the caller's permissions.

{ "values": { "name": "Asha", "email": "asha@example.com", "message": "Hello" } }

Response 201 with the submission object. 400 VALIDATION_ERROR names the field that failed. The form must be published (400 VALIDATION_ERROR: "this form is not published yet").

POST /orgs/{orgId}/forms/generate

Body {"prompt": "a booking enquiry with dates and party size"}. Returns {"fields": [ … ]} as a proposal. Nothing is saved. Answers 503 AGENT_DISABLED when AI drafting is unavailable.