Skip to content
DocsDashboard

Forms (form)

A short general form. Use it only when no more specific page type fits. Do not ask for payment card details, passwords or other credentials, or identity-document numbers: such fields are rejected.

When to use it: You need more than two typed values at once (name, phone, a choice, a note). For one value, ask in chat.

When not to use it: Do not use it for one or two questions (ask in chat), or when a more specific page type fits: dates, times, products or a choice between options.

Gather first:

  • Exactly which details you still need; do not ask for what you already know.
  • Whether each field is required.

Writing good content:

  • Keep it short: ask only what you need now, at most 12 fields. Split longer needs into a later page.
  • Pick the kind that matches the answer (email, phone, number) so the right keyboard opens, and select with options when there is a fixed list.
  • Write labels as plain questions, and use helpText for why you need something or its format.
  • Never ask for card numbers, passwords or other credentials, or identity-document numbers: these are refused, and a form is not a way around that.

Fields

Every page also has version (always 1), title, and optionally description, branding, cover (a header photo) and expiresAt; see Page fields. These are the fields of page (the page type and its data):

FieldDescription
fieldsobject[], required. The fields, in display order (1-12). For more, use several pages. Limits: 1-12 items.
fields[].idstring, required. Stable identifier you choose. It is returned in the result, so keep it meaningful to you. Limits: matches ^[A-Za-z0-9_-]{1,64}$.
fields[].labelstring, required. Question or field name shown above the input, e.g. "Delivery address". Limits: 1-120 characters.
fields[].kind"short_text" | "long_text" | "number" | "email" | "phone" | "select" | "toggle", required. short_text: one line. long_text: several lines. number: numeric keypad. email, phone: matching keyboards. select: dropdown, needs options. toggle: a yes/no checkbox; with required true it must be ticked (e.g. "I agree").
fields[].requiredboolean. true blocks submit until the field is filled (for a toggle: until it is ticked). Omit or false for an optional field.
fields[].placeholderstring. Example text shown inside an empty input. Ignored for toggle and select. Limits: 1-120 characters.
fields[].helpTextstring. Optional hint shown under the field. Limits: 1-280 characters.
fields[].minnumber. number only: the smallest allowed value.
fields[].maxnumber. number only: the largest allowed value.
fields[].maxLengthinteger. short_text and long_text only: most characters allowed. Defaults to 200 (short_text) or 2000 (long_text). Limits: 1-5000.
fields[].optionsobject[]. select only: 2-30 choices. The chosen option id is returned as the value. Limits: 2-30 items.
fields[].options[].idstring, required. Stable identifier you choose. It is returned in the result, so keep it meaningful to you. Limits: matches ^[A-Za-z0-9_-]{1,64}$.
fields[].options[].labelstring, required. Option text. Limits: 1-120 characters.
fields[].autocomplete"name" | "given-name" | "family-name" | "organization" | "street-address" | "postal-code". short_text only: browser autofill hint, e.g. "name" or "postal-code". email and phone are handled automatically.

Why these are required: kind decides the input and the value type in the result.

When optional fields are left out: The field is optional; no placeholder or hint; text limits default to 200 (short) or 2000 (long) characters.

Minimal example

Only the required fields:

{
  "version": 1,
  "title": "Tell us about your visit",
  "page": {
    "type": "form",
    "fields": [
      {
        "id": "name",
        "label": "Your name",
        "kind": "short_text"
      },
      {
        "id": "email",
        "label": "Email",
        "kind": "email"
      },
      {
        "id": "feedback",
        "label": "What could we do better?",
        "kind": "long_text"
      }
    ]
  }
}

Full example

{
  "version": 1,
  "title": "Delivery details",
  "description": "I need these to schedule your couch delivery.",
  "branding": {
    "name": "Juniper Concierge",
    "accentColor": "#0050d8"
  },
  "cover": {
    "url": "https://images.example.test/couch.jpg",
    "alt": "A green three-seat couch in a bright living room"
  },
  "page": {
    "type": "form",
    "fields": [
      {
        "id": "name",
        "label": "Full name",
        "kind": "short_text",
        "required": true,
        "autocomplete": "name"
      },
      {
        "id": "phone",
        "label": "Mobile number",
        "kind": "phone",
        "required": true,
        "helpText": "The driver will text you before arriving."
      },
      {
        "id": "email",
        "label": "Email",
        "kind": "email",
        "required": false
      },
      {
        "id": "floor",
        "label": "Floor number",
        "kind": "number",
        "required": true,
        "min": 0,
        "max": 60,
        "placeholder": "0"
      },
      {
        "id": "window",
        "label": "Delivery window",
        "kind": "select",
        "required": true,
        "options": [
          {
            "id": "am",
            "label": "Morning (8-12)"
          },
          {
            "id": "pm",
            "label": "Afternoon (12-5)"
          },
          {
            "id": "eve",
            "label": "Evening (5-8)"
          }
        ]
      },
      {
        "id": "notes",
        "label": "Anything the driver should know?",
        "kind": "long_text",
        "required": false,
        "maxLength": 300,
        "placeholder": "Gate code, pets, parking..."
      },
      {
        "id": "adult",
        "label": "Someone over 18 will be home",
        "kind": "toggle",
        "required": true
      },
      {
        "id": "updates",
        "label": "Text me delivery updates",
        "kind": "toggle",
        "required": false
      }
    ]
  }
}

Result

When the person submits, the result has these fields:

FieldDescription
valuesobject, required. Answers keyed by field id. Optional fields left empty are omitted; toggles are always present.

For the full example above, a result could be:

{
  "values": {
    "name": "Ada Lovelace",
    "phone": "+1 415 555 0132",
    "email": "ada@example.com",
    "floor": 3,
    "window": "am",
    "notes": "Leave it with the doorman",
    "adult": true,
    "updates": false
  }
}

Send it

eve, AI SDK and MCP

With @pagesystems/eve, @pagesystems/ai-sdk or the MCP server, the model calls get_page_type_schema with "form" (the same JSON as GET /v1/page-types/form), then create_page with this input (no version or branding; branding comes from your dashboard):

{
  "title": "Delivery details",
  "description": "I need these to schedule your couch delivery.",
  "cover": {
    "url": "https://images.example.test/couch.jpg",
    "alt": "A green three-seat couch in a bright living room"
  },
  "page": {
    "type": "form",
    "fields": [
      {
        "id": "name",
        "label": "Full name",
        "kind": "short_text",
        "required": true,
        "autocomplete": "name"
      },
      {
        "id": "phone",
        "label": "Mobile number",
        "kind": "phone",
        "required": true,
        "helpText": "The driver will text you before arriving."
      },
      {
        "id": "email",
        "label": "Email",
        "kind": "email",
        "required": false
      },
      {
        "id": "floor",
        "label": "Floor number",
        "kind": "number",
        "required": true,
        "min": 0,
        "max": 60,
        "placeholder": "0"
      },
      {
        "id": "window",
        "label": "Delivery window",
        "kind": "select",
        "required": true,
        "options": [
          {
            "id": "am",
            "label": "Morning (8-12)"
          },
          {
            "id": "pm",
            "label": "Afternoon (12-5)"
          },
          {
            "id": "eve",
            "label": "Evening (5-8)"
          }
        ]
      },
      {
        "id": "notes",
        "label": "Anything the driver should know?",
        "kind": "long_text",
        "required": false,
        "maxLength": 300,
        "placeholder": "Gate code, pets, parking..."
      },
      {
        "id": "adult",
        "label": "Someone over 18 will be home",
        "kind": "toggle",
        "required": true
      },
      {
        "id": "updates",
        "label": "Text me delivery updates",
        "kind": "toggle",
        "required": false
      }
    ]
  }
}

REST

curl https://pagesystems.ai/v1/pages \
  -H "Authorization: Bearer $PAGE_SYSTEMS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"spec":{"version":1,"title":"Delivery details","description":"I need these to schedule your couch delivery.","branding":{"name":"Juniper Concierge","accentColor":"#0050d8"},"cover":{"url":"https://images.example.test/couch.jpg","alt":"A green three-seat couch in a bright living room"},"page":{"type":"form","fields":[{"id":"name","label":"Full name","kind":"short_text","required":true,"autocomplete":"name"},{"id":"phone","label":"Mobile number","kind":"phone","required":true,"helpText":"The driver will text you before arriving."},{"id":"email","label":"Email","kind":"email","required":false},{"id":"floor","label":"Floor number","kind":"number","required":true,"min":0,"max":60,"placeholder":"0"},{"id":"window","label":"Delivery window","kind":"select","required":true,"options":[{"id":"am","label":"Morning (8-12)"},{"id":"pm","label":"Afternoon (12-5)"},{"id":"eve","label":"Evening (5-8)"}]},{"id":"notes","label":"Anything the driver should know?","kind":"long_text","required":false,"maxLength":300,"placeholder":"Gate code, pets, parking..."},{"id":"adult","label":"Someone over 18 will be home","kind":"toggle","required":true},{"id":"updates","label":"Text me delivery updates","kind":"toggle","required":false}]}}}'

Validation errors

A spec that does not validate is refused with 400 invalid_spec. The message names each problem and its path, so a model can fix the spec and try again. For example:

Leaving out the required field fields:

✖ Invalid input: expected array, received undefined
  → at page.fields

Misspelling a field name (unknown keys are rejected):

✖ Unrecognized key: "colour"
  → at page