---
title: "Create Form"
method: POST
path: "/forms"
tags: ["Forms"]
---

# Create Form

`POST /forms`

## Request

- Base URL: `https://api.pinnacle.sh`
- URL: `https://api.pinnacle.sh/forms`
- Auth: API key in header `PINNACLE-API-KEY`

Create a hosted form without sending it. <br>

Returns the form object including its public URL — `https://forms.pinnacle.sh/{form_id}`. <br>

To also deliver the URL to a recipient over SMS or RCS in a single call, use [`POST /forms/send`](/api-reference/forms/send-form).

## Request body

- CreateFormRequest: Body of `POST /forms`. Same shape as `FormDefinition`.
  - `name` string: Human-readable name for the form. Rendered as the form's title.
  - `description` string: Longer description rendered below the title.
  - `fields` FormField[], required: Ordered list of fields the recipient will fill out. At least one field is required.
    - union: A single field definition inside a form. Discriminated by `type`.
      - object: Single-line text input with optional length bounds and regex pattern.
        - `type` 'text', required: Literal `"text"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
        - `pattern` string: Regex the value must match. Validated server-side with RE2, so patterns that enable catastrophic backtracking (backreferences, lookarounds) are rejected at form creation.
        - `min_length` integer: Minimum number of characters the submitter must enter.
        - `max_length` integer: Maximum number of characters the submitter may enter.
      - object: Multi-line text input for longer free-form answers.
        - `type` 'textarea', required: Literal `"textarea"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
        - `pattern` string: Regex the value must match. Same RE2 safety check as `TextField.pattern`.
        - `min_length` integer: Minimum number of characters the submitter must enter.
        - `max_length` integer: Maximum number of characters the submitter may enter.
      - object: Free-form text input backed by a client-side Google Places autocomplete. Shares text-field validation (length / pattern); the place lookup happens entirely in the browser and is purely for suggestion UX.
        - `type` 'address', required: Literal `"address"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
        - `pattern` string: Regex the value must match. Same RE2 safety check as `TextField.pattern`. Rarely needed for addresses — usually left off.
        - `min_length` integer: Minimum number of characters in the selected address.
        - `max_length` integer: Maximum number of characters in the selected address.
      - object: Email input. Server rejects values that fail RFC 5322 validation.
        - `type` 'email', required: Literal `"email"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
      - object: URL input. Server requires a valid absolute URL.
        - `type` 'url', required: Literal `"url"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
      - object: Phone input. Client formats the value as the user types ("(555) 555-1212") and normalizes to E.164 ("+15555551212") before submission.
        - `type` 'phone', required: Literal `"phone"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
      - object: Numeric input with optional `min` / `max` bounds and `step` granularity.
        - `type` 'number', required: Literal `"number"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
        - `min` number, double: Minimum accepted value.
        - `max` number, double: Maximum accepted value.
        - `step` number, double: Increment between valid values. For example, `step: 0.5` lets the user enter 1, 1.5, 2, etc.
      - object: Slider input. `min`/`max` define the bounds, `step` the granularity.
        - `type` 'range', required: Literal `"range"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `min` number, double: Lower bound of the slider.
        - `max` number, double: Upper bound of the slider.
        - `step` number, double: Increment between adjacent slider stops.
      - object: Star rating 1..`max`. Submitted value is the selected integer.
        - `type` 'rating', required: Literal `"rating"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `min` integer: Minimum allowed rating. `0` means the submitter can clear their selection.
        - `max` integer: Number of stars rendered.
      - object: Date picker. `min`/`max` accept `YYYY-MM-DD`.
        - `type` 'date', required: Literal `"date"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
        - `min` string: Earliest selectable date as `YYYY-MM-DD`.
        - `max` string: Latest selectable date as `YYYY-MM-DD`.
      - object: Time picker. `min`/`max` accept `HH:MM` (24-hour).
        - `type` 'time', required: Literal `"time"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
        - `min` string: Earliest selectable time as `HH:MM` (24-hour).
        - `max` string: Latest selectable time as `HH:MM` (24-hour).
      - object: Local-datetime picker. `min`/`max` accept ISO 8601 timestamps.
        - `type` 'datetime', required: Literal `"datetime"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
        - `min` string, date-time: Earliest selectable instant as an ISO 8601 timestamp.
        - `max` string, date-time: Latest selectable instant as an ISO 8601 timestamp.
      - object: Native color picker. Submitted value is a 6-digit hex string like `#ff6b35`.
        - `type` 'color', required: Literal `"color"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
      - object: Single-select dropdown.
        - `type` 'select', required: Literal `"select"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `options` FormFieldOption[], required: Choices shown in the dropdown. At least one is required.
          - `value` string, required: Machine value sent back in the submission payload.
          - `label` string, required: Human label rendered in the form.
        - `placeholder` string: Placeholder row shown when no option is selected (e.g. "Select…").
      - object: Single-select radio group.
        - `type` 'radio', required: Literal `"radio"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `options` FormFieldOption[], required: Choices shown as radio buttons. At least one is required.
          - `value` string, required: Machine value sent back in the submission payload.
          - `label` string, required: Human label rendered in the form.
      - object: Multi-select checkbox group when `options` is provided. If `options` is omitted the field renders as a single boolean checkbox.
        - `type` 'checkbox', required: Literal `"checkbox"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `options` FormFieldOption[]: Choices shown as checkboxes. Omit to render a single boolean checkbox.
          - `value` string, required: Machine value sent back in the submission payload.
          - `label` string, required: Human label rendered in the form.
        - `min_selected` integer: Minimum number of boxes the submitter must check.
        - `max_selected` integer: Maximum number of boxes the submitter may check. Extra boxes become uninteractive once the cap is reached.
  - `can_update` boolean: If true, the recipient can reopen the submission URL and edit their answers. When false (default), the submission URL becomes read-only (`Already submitted`) after the first submit.
  - `expires_at` string, date-time: After this timestamp the form stops accepting submissions. Omit for no expiration.
  - `theme_override` FormThemeOverride: Sparse override layered on top of your team's theme. Every field is optional; provided fields replace the corresponding team defaults at render time. URL fields must use `https://`.
    - `logo_url` string, uri, nullable: Logo displayed at the top of the form. Square images render as an inline mark; wide landscape images render as a full-width banner.
    - `favicon_url` string, uri, nullable: Favicon shown in the browser tab when the form page loads.
    - `theme_mode` 'light' | 'dark' | 'auto': Rendering mode. `light` forces the light palette, `dark` forces dark, and `auto` follows the respondent's OS-level appearance setting.
    - `colors` FormThemeOverrideColors: Per-mode palette overrides. Each mode is an object with optional `primary`, `background`, and `text` hex colors. Omit a mode to inherit the team default for it.
      - `light` FormColorPalette: Per-mode palette. All three colors are optional; any you omit fall back to the team default for that mode.
        - `primary` string: Accent color used for the submit button, focus rings, filled checkboxes, and other interactive surfaces.
        - `background` string: Page background color. Fallback solid color used by Safari chrome tinting and safe-area regions.
        - `text` string: Body text and label color.
      - `dark` FormColorPalette: Per-mode palette. All three colors are optional; any you omit fall back to the team default for that mode.
        - `primary` string: Accent color used for the submit button, focus rings, filled checkboxes, and other interactive surfaces.
        - `background` string: Page background color. Fallback solid color used by Safari chrome tinting and safe-area regions.
        - `text` string: Body text and label color.
    - `font_family` 'inter' | 'system' | 'serif' | 'mono' | 'rounded': Typeface stack. `system` uses the respondent's OS default sans-serif.
    - `corner_radius` 'sharp' | 'rounded' | 'pill': Border-radius preset for cards, inputs, and buttons.
    - `content_alignment` 'left' | 'center' | 'right': Horizontal alignment for the form header (title, description, logo).
    - `background` union: Background layer for the form page. Discriminated by `type`.
      - object: No background effect — the form page is filled with a single flat color. The color itself isn't specified on this object; it's read from the sibling `colors` field on the same `theme_override` object (i.e. `theme_override.colors.light.background` in light mode, `theme_override.colors.dark.background` in dark mode). If you don't override `colors`, the color falls back to the one your team has configured in the [Pinnacle Dashboard](https://app.pinnacle.sh/dashboard/forms). Complete example — a POST `/forms` body that sets a white form in light mode and near-black in dark mode with no background effect: ```json { "name": "Contact request", "fields": [ { "type": "text", "key": "full_name", "label": "Full name", "required": true } ], "theme_override": { "background": { "type": "solid" }, "colors": { "light": { "background": "#ffffff" }, "dark": { "background": "#0a0a0a" } } } } ``` Pick `solid` when you want a clean one-color page with no gradient / image / pattern overlay. Use `gradient`, `image`, or `pattern` instead when you want something layered on top of the base color.
        - `type` 'solid', required: Discriminator value: solid
      - object: Two-stop linear gradient. Supply separate light- and dark-mode gradients; the renderer picks based on `theme_mode`.
        - `type` 'gradient', required: Discriminator value: gradient
        - `light` FormGradient, required: A single light- or dark-mode gradient stop pair.
          - `from` string, required: Starting color of the gradient.
          - `to` string, required: Ending color of the gradient.
          - `angle` 'top-bottom' | 'diagonal', required: Direction the gradient runs. `top-bottom` is vertical; `diagonal` runs top-left → bottom-right.
        - `dark` FormGradient, required: A single light- or dark-mode gradient stop pair.
          - `from` string, required: Starting color of the gradient.
          - `to` string, required: Ending color of the gradient.
          - `angle` 'top-bottom' | 'diagonal', required: Direction the gradient runs. `top-bottom` is vertical; `diagonal` runs top-left → bottom-right.
      - object: Image background. The image is tiled or scaled across the page with optional blur and dark tint overlay.
        - `type` 'image', required: Discriminator value: image
        - `url` string, uri, required: HTTPS URL of the background image. Must start with `https://`.
        - `tint_opacity` integer, required: Darkening overlay intensity from 0 (no overlay) to 80 (heavy). Helps preserve text contrast over busy images.
        - `blur` boolean, required: When true, blurs the background image.
        - `zoom` integer, required: Scale percentage. 100 is native size; higher values crop the image tighter.
      - object: Subtle repeating SVG pattern tinted by the palette's foreground color.
        - `type` 'pattern', required: Discriminator value: pattern
        - `preset` 'noise' | 'dots' | 'mesh' | 'grid' | 'waves' | 'topo', required: Which built-in pattern to render.
    - `submit_button_label` string: Text shown on the submit button. Defaults to `Submit`.
    - `success_message` string: Message shown on the success panel after a successful submit (unless `redirect_url` is set, in which case the respondent is redirected).
    - `redirect_url` string, uri, nullable: When set, the submitter is redirected here after a successful submit instead of seeing the success panel.
    - `privacy_url` string, uri, nullable: Link target for the `Privacy` footer link. Omit or set to null to hide the link.
    - `terms_url` string, uri, nullable: Link target for the `Terms` footer link. Omit or set to null to hide the link.
    - `og_image_url` string, uri, nullable: Image shown when the form URL is shared in social / messaging link previews (Open Graph).
    - `og_description` string, nullable: Open Graph description used in link previews. Falls back to the form's `description` when unset.

## Response `200`

Form created.

- Form: A hosted form definition.
  - `id` string, required: Form id (starts with `form_`).
  - `url` string, required: Public shareable URL of the form (`https://forms.pinnacle.sh/{form_id}`).
  - `name` string, nullable, required: Human-readable name for the form. Rendered as the form's title.
  - `description` string, nullable, required: Longer description rendered below the title.
  - `fields` FormField[], required: Ordered list of fields the recipient fills out.
    - union: A single field definition inside a form. Discriminated by `type`.
      - object: Single-line text input with optional length bounds and regex pattern.
        - `type` 'text', required: Literal `"text"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
        - `pattern` string: Regex the value must match. Validated server-side with RE2, so patterns that enable catastrophic backtracking (backreferences, lookarounds) are rejected at form creation.
        - `min_length` integer: Minimum number of characters the submitter must enter.
        - `max_length` integer: Maximum number of characters the submitter may enter.
      - object: Multi-line text input for longer free-form answers.
        - `type` 'textarea', required: Literal `"textarea"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
        - `pattern` string: Regex the value must match. Same RE2 safety check as `TextField.pattern`.
        - `min_length` integer: Minimum number of characters the submitter must enter.
        - `max_length` integer: Maximum number of characters the submitter may enter.
      - object: Free-form text input backed by a client-side Google Places autocomplete. Shares text-field validation (length / pattern); the place lookup happens entirely in the browser and is purely for suggestion UX.
        - `type` 'address', required: Literal `"address"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
        - `pattern` string: Regex the value must match. Same RE2 safety check as `TextField.pattern`. Rarely needed for addresses — usually left off.
        - `min_length` integer: Minimum number of characters in the selected address.
        - `max_length` integer: Maximum number of characters in the selected address.
      - object: Email input. Server rejects values that fail RFC 5322 validation.
        - `type` 'email', required: Literal `"email"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
      - object: URL input. Server requires a valid absolute URL.
        - `type` 'url', required: Literal `"url"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
      - object: Phone input. Client formats the value as the user types ("(555) 555-1212") and normalizes to E.164 ("+15555551212") before submission.
        - `type` 'phone', required: Literal `"phone"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
      - object: Numeric input with optional `min` / `max` bounds and `step` granularity.
        - `type` 'number', required: Literal `"number"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
        - `min` number, double: Minimum accepted value.
        - `max` number, double: Maximum accepted value.
        - `step` number, double: Increment between valid values. For example, `step: 0.5` lets the user enter 1, 1.5, 2, etc.
      - object: Slider input. `min`/`max` define the bounds, `step` the granularity.
        - `type` 'range', required: Literal `"range"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `min` number, double: Lower bound of the slider.
        - `max` number, double: Upper bound of the slider.
        - `step` number, double: Increment between adjacent slider stops.
      - object: Star rating 1..`max`. Submitted value is the selected integer.
        - `type` 'rating', required: Literal `"rating"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `min` integer: Minimum allowed rating. `0` means the submitter can clear their selection.
        - `max` integer: Number of stars rendered.
      - object: Date picker. `min`/`max` accept `YYYY-MM-DD`.
        - `type` 'date', required: Literal `"date"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
        - `min` string: Earliest selectable date as `YYYY-MM-DD`.
        - `max` string: Latest selectable date as `YYYY-MM-DD`.
      - object: Time picker. `min`/`max` accept `HH:MM` (24-hour).
        - `type` 'time', required: Literal `"time"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
        - `min` string: Earliest selectable time as `HH:MM` (24-hour).
        - `max` string: Latest selectable time as `HH:MM` (24-hour).
      - object: Local-datetime picker. `min`/`max` accept ISO 8601 timestamps.
        - `type` 'datetime', required: Literal `"datetime"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `placeholder` string: Ghost text shown inside the input while it's empty.
        - `min` string, date-time: Earliest selectable instant as an ISO 8601 timestamp.
        - `max` string, date-time: Latest selectable instant as an ISO 8601 timestamp.
      - object: Native color picker. Submitted value is a 6-digit hex string like `#ff6b35`.
        - `type` 'color', required: Literal `"color"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
      - object: Single-select dropdown.
        - `type` 'select', required: Literal `"select"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `options` FormFieldOption[], required: Choices shown in the dropdown. At least one is required.
          - `value` string, required: Machine value sent back in the submission payload.
          - `label` string, required: Human label rendered in the form.
        - `placeholder` string: Placeholder row shown when no option is selected (e.g. "Select…").
      - object: Single-select radio group.
        - `type` 'radio', required: Literal `"radio"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `options` FormFieldOption[], required: Choices shown as radio buttons. At least one is required.
          - `value` string, required: Machine value sent back in the submission payload.
          - `label` string, required: Human label rendered in the form.
      - object: Multi-select checkbox group when `options` is provided. If `options` is omitted the field renders as a single boolean checkbox.
        - `type` 'checkbox', required: Literal `"checkbox"` — discriminates this variant inside `FormField`.
        - `key` string, required: Programmatic key used as the property name in the submission payload. Must start with a letter or underscore; letters, digits, and underscores only.
        - `label` string, required: Text shown above the input.
        - `required` boolean: When true, the submitter must provide a value. Default false.
        - `help_text` string: Small caption rendered under the input to give the submitter extra context.
        - `options` FormFieldOption[]: Choices shown as checkboxes. Omit to render a single boolean checkbox.
          - `value` string, required: Machine value sent back in the submission payload.
          - `label` string, required: Human label rendered in the form.
        - `min_selected` integer: Minimum number of boxes the submitter must check.
        - `max_selected` integer: Maximum number of boxes the submitter may check. Extra boxes become uninteractive once the cap is reached.
  - `can_update` boolean, required: When true, the recipient can reopen the submission URL and edit their answers. When false, the URL becomes read-only after the first submit.
  - `expires_at` string, date-time, nullable, required: After this timestamp the form stops accepting submissions. Null means no expiration.
  - `theme_override` FormThemeOverride, required: Sparse override layered on top of your team's theme. Every field is optional; provided fields replace the corresponding team defaults at render time. URL fields must use `https://`.
    - `logo_url` string, uri, nullable: Logo displayed at the top of the form. Square images render as an inline mark; wide landscape images render as a full-width banner.
    - `favicon_url` string, uri, nullable: Favicon shown in the browser tab when the form page loads.
    - `theme_mode` 'light' | 'dark' | 'auto': Rendering mode. `light` forces the light palette, `dark` forces dark, and `auto` follows the respondent's OS-level appearance setting.
    - `colors` FormThemeOverrideColors: Per-mode palette overrides. Each mode is an object with optional `primary`, `background`, and `text` hex colors. Omit a mode to inherit the team default for it.
      - `light` FormColorPalette: Per-mode palette. All three colors are optional; any you omit fall back to the team default for that mode.
        - `primary` string: Accent color used for the submit button, focus rings, filled checkboxes, and other interactive surfaces.
        - `background` string: Page background color. Fallback solid color used by Safari chrome tinting and safe-area regions.
        - `text` string: Body text and label color.
      - `dark` FormColorPalette: Per-mode palette. All three colors are optional; any you omit fall back to the team default for that mode.
        - `primary` string: Accent color used for the submit button, focus rings, filled checkboxes, and other interactive surfaces.
        - `background` string: Page background color. Fallback solid color used by Safari chrome tinting and safe-area regions.
        - `text` string: Body text and label color.
    - `font_family` 'inter' | 'system' | 'serif' | 'mono' | 'rounded': Typeface stack. `system` uses the respondent's OS default sans-serif.
    - `corner_radius` 'sharp' | 'rounded' | 'pill': Border-radius preset for cards, inputs, and buttons.
    - `content_alignment` 'left' | 'center' | 'right': Horizontal alignment for the form header (title, description, logo).
    - `background` union: Background layer for the form page. Discriminated by `type`.
      - object: No background effect — the form page is filled with a single flat color. The color itself isn't specified on this object; it's read from the sibling `colors` field on the same `theme_override` object (i.e. `theme_override.colors.light.background` in light mode, `theme_override.colors.dark.background` in dark mode). If you don't override `colors`, the color falls back to the one your team has configured in the [Pinnacle Dashboard](https://app.pinnacle.sh/dashboard/forms). Complete example — a POST `/forms` body that sets a white form in light mode and near-black in dark mode with no background effect: ```json { "name": "Contact request", "fields": [ { "type": "text", "key": "full_name", "label": "Full name", "required": true } ], "theme_override": { "background": { "type": "solid" }, "colors": { "light": { "background": "#ffffff" }, "dark": { "background": "#0a0a0a" } } } } ``` Pick `solid` when you want a clean one-color page with no gradient / image / pattern overlay. Use `gradient`, `image`, or `pattern` instead when you want something layered on top of the base color.
        - `type` 'solid', required: Discriminator value: solid
      - object: Two-stop linear gradient. Supply separate light- and dark-mode gradients; the renderer picks based on `theme_mode`.
        - `type` 'gradient', required: Discriminator value: gradient
        - `light` FormGradient, required: A single light- or dark-mode gradient stop pair.
          - `from` string, required: Starting color of the gradient.
          - `to` string, required: Ending color of the gradient.
          - `angle` 'top-bottom' | 'diagonal', required: Direction the gradient runs. `top-bottom` is vertical; `diagonal` runs top-left → bottom-right.
        - `dark` FormGradient, required: A single light- or dark-mode gradient stop pair.
          - `from` string, required: Starting color of the gradient.
          - `to` string, required: Ending color of the gradient.
          - `angle` 'top-bottom' | 'diagonal', required: Direction the gradient runs. `top-bottom` is vertical; `diagonal` runs top-left → bottom-right.
      - object: Image background. The image is tiled or scaled across the page with optional blur and dark tint overlay.
        - `type` 'image', required: Discriminator value: image
        - `url` string, uri, required: HTTPS URL of the background image. Must start with `https://`.
        - `tint_opacity` integer, required: Darkening overlay intensity from 0 (no overlay) to 80 (heavy). Helps preserve text contrast over busy images.
        - `blur` boolean, required: When true, blurs the background image.
        - `zoom` integer, required: Scale percentage. 100 is native size; higher values crop the image tighter.
      - object: Subtle repeating SVG pattern tinted by the palette's foreground color.
        - `type` 'pattern', required: Discriminator value: pattern
        - `preset` 'noise' | 'dots' | 'mesh' | 'grid' | 'waves' | 'topo', required: Which built-in pattern to render.
    - `submit_button_label` string: Text shown on the submit button. Defaults to `Submit`.
    - `success_message` string: Message shown on the success panel after a successful submit (unless `redirect_url` is set, in which case the respondent is redirected).
    - `redirect_url` string, uri, nullable: When set, the submitter is redirected here after a successful submit instead of seeing the success panel.
    - `privacy_url` string, uri, nullable: Link target for the `Privacy` footer link. Omit or set to null to hide the link.
    - `terms_url` string, uri, nullable: Link target for the `Terms` footer link. Omit or set to null to hide the link.
    - `og_image_url` string, uri, nullable: Image shown when the form URL is shared in social / messaging link previews (Open Graph).
    - `og_description` string, nullable: Open Graph description used in link previews. Falls back to the form's `description` when unset.
  - `submission_count` integer, required: Count of distinct completed submissions for this form. Does not increment on edits to an existing `can_update=true` submission.
  - `last_submitted_at` string, date-time, nullable, required: Timestamp of the most recent submission event. Updated on each new submit and on each edit of a `can_update=true` submission.
  - `archived_at` string, date-time, nullable, required: When set, the form is archived (soft-deleted) and cannot accept new submissions or be updated. Restore by PATCHing `archived_at` back to null.
  - `created_at` string, date-time, required: ISO 8601 timestamp of when the form was created.
  - `updated_at` string, date-time, required: ISO 8601 timestamp of the most recent form mutation.

## Other responses

- `400`: Validation failed. The payload has missing required fields and/or invalid types. <br> See https://zod.dev/error-formatting for more information.
- `401`: The request lacks valid authentication credentials or the provided credentials are invalid. <br> Ensure you're including a valid API key in the request headers and that your account has the necessary permissions to access this endpoint.
- `500`: An unexpected error occurred on Pinnacle's servers while processing your request. <br> If this error persists, please contact support with the request details.

---

[API](https://skmtc.dev/pinnacle/apis/pinnacle-2.md) - [All operations](https://skmtc.dev/pinnacle/apis/pinnacle-2/llms.txt) - [OpenAPI document](https://skmtc.dev/pinnacle/apis/pinnacle-2/revisions/45416dadab79?raw)
