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

# Send Form

`POST /forms/send`

## Request

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

Send a form to a recipient over SMS or RCS, or mint a standalone submission URL.

Pass `form` as either an existing form id (`form_*`) or an inline `{ fields, ... }` definition to mint a new form for this send.

The delivery channel is inferred from `from`:
- `from: "agent_*"` → RCS (with optional SMS `fallback`)
- `from: "+E.164"` → SMS

When `to` is provided, Pinnacle dispatches a message whose body contains the submission URL and the recipient is recorded on the response: `submission.to` echoes the same E.164 number and `message_id` is the id of the outbound SMS/RCS. 

When `to` is omitted, no message is sent — `submission.to` and `message_id` are both `null` — which is useful for embedding the URL in your own outreach.

On completion, a `FORM.SUBMISSION` webhook event is delivered to webhooks subscribed to the sender. See [Receiving Message, Call, and User Events](/guides/messages/receiving).

## Request body

- union: Send a form to a recipient over SMS or RCS in one call, or mint a standalone submission URL when `to` is omitted. Picks the SMS or RCS branch based on the shape of `from` (agent_* → RCS, E.164 → SMS). When `to` is provided, Pinnacle dispatches the message immediately and the recipient is recorded on the response (`submission.to` is set to the same E.164 number, and `message_id` is the id of the outbound SMS/RCS). When `to` is omitted, no message is sent — `submission.to` and `message_id` are both `null` and only the submission URL is returned.
  - SendFormViaRcsRequest: Send a form from an RCS agent. `from` must be an `agent_*` id.
    - `from` string, required: RCS agent id (`agent_*`).
    - `to` string: Recipient phone in E.164. When present Pinnacle delivers the form URL via RCS (or the configured fallback) and FORM.SUBMISSION fires with `submission.to` populated. When omitted the call returns a submission URL without dispatching any message and the webhook later fires with `submission.to: null`.
    - `form` union, required: Existing form id or an inline definition that mints a new form for this send.
      - string: Existing form id (`form_*`) to reuse.
      - SendFormViaRcsRequestForm1: The minimum shape needed to create a form. Used by `POST /forms` and inline by `POST /forms/send`.
        - `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.
              - …
            - object: Multi-line text input for longer free-form answers.
              - …
            - 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.
              - …
            - object: Email input. Server rejects values that fail RFC 5322 validation.
              - …
            - object: URL input. Server requires a valid absolute URL.
              - …
            - object: Phone input. Client formats the value as the user types ("(555) 555-1212") and normalizes to E.164 ("+15555551212") before submission.
              - …
            - object: Numeric input with optional `min` / `max` bounds and `step` granularity.
              - …
            - object: Slider input. `min`/`max` define the bounds, `step` the granularity.
              - …
            - object: Star rating 1..`max`. Submitted value is the selected integer.
              - …
            - object: Date picker. `min`/`max` accept `YYYY-MM-DD`.
              - …
            - object: Time picker. `min`/`max` accept `HH:MM` (24-hour).
              - …
            - object: Local-datetime picker. `min`/`max` accept ISO 8601 timestamps.
              - …
            - object: Native color picker. Submitted value is a 6-digit hex string like `#ff6b35`.
              - …
            - object: Single-select dropdown.
              - …
            - object: Single-select radio group.
              - …
            - object: Multi-select checkbox group when `options` is provided. If `options` is omitted the field renders as a single boolean checkbox.
              - …
        - `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.
              - …
            - `dark` FormColorPalette: Per-mode palette. All three colors are optional; any you omit fall back to the team default for that mode.
              - …
          - `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.
              - …
            - object: Two-stop linear gradient. Supply separate light- and dark-mode gradients; the renderer picks based on `theme_mode`.
              - …
            - object: Image background. The image is tiled or scaled across the page with optional blur and dark tint overlay.
              - …
            - object: Subtle repeating SVG pattern tinted by the palette's foreground color.
              - …
          - `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.
    - `fallback` SendFormViaRcsRequestFallback: When RCS is unavailable on the recipient's device, fall back to SMS from this number. Requires `to`.
      - `from` string, required: SMS phone in E.164 to deliver from when the recipient can't receive RCS.
    - `options` SendFormOptions: Optional delivery knobs shared by RCS and SMS sends.
      - `schedule` ScheduleSchema: Define when and how your message should be sent.
        - `sendAt` string, required: The date and time you want your message to be sent. - Use [ISO 8601 format](https://en.wikipedia.org/wiki/ISO_8601), for example: `2024-12-17T14:30:00Z`. Format: `YYYY-MM-DDThh:mm` (e.g., `2024-12-25T14:30`). - This time must be at least 5 minutes in the future. - The message will be scheduled based on the `timezone` you provide. - If you set a `recurrence` schedule, this is the start date and time for the recurring schedule.
        - `recurrence` string: AWS cron expression for recurring schedules (6 fields). <br> [Learn more about cron expressions](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-scheduled-rule-pattern.html). <br> **Examples:** - `0 10 * * ? *` - Every day at 10:00 AM - `0/30 * * * ? *` - Every 30 minutes - `0 9 ? * MON-FRI *` - Every weekday at 9:00 AM - `0 12 1 * ? *` - First day of every month at noon
        - `timezone` string: IANA timezone identifier (e.g., `America/New_York`, `UTC`). <br> Defaults to `UTC` if not specified.
        - `endDate` string: Date and time when recurring messages should stop. <br> Format: `YYYY-MM-DDThh:mm`. Required if `recurrence` is set.
      - `webview_mode` 'FULL' | 'HALF' | 'TALL': RCS webview size for the open-url button action. Ignored for SMS.
  - SendFormViaSmsRequest: Send a form from a phone number. `from` must be an E.164 phone.
    - `from` string, required: Sender phone in E.164.
    - `to` string: Recipient phone in E.164. Omit to mint a URL-only form without sending a message.
    - `form` union, required
      - string: Existing form id (`form_*`) to reuse.
      - SendFormViaSmsRequestForm1: The minimum shape needed to create a form. Used by `POST /forms` and inline by `POST /forms/send`.
        - `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.
              - …
            - object: Multi-line text input for longer free-form answers.
              - …
            - 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.
              - …
            - object: Email input. Server rejects values that fail RFC 5322 validation.
              - …
            - object: URL input. Server requires a valid absolute URL.
              - …
            - object: Phone input. Client formats the value as the user types ("(555) 555-1212") and normalizes to E.164 ("+15555551212") before submission.
              - …
            - object: Numeric input with optional `min` / `max` bounds and `step` granularity.
              - …
            - object: Slider input. `min`/`max` define the bounds, `step` the granularity.
              - …
            - object: Star rating 1..`max`. Submitted value is the selected integer.
              - …
            - object: Date picker. `min`/`max` accept `YYYY-MM-DD`.
              - …
            - object: Time picker. `min`/`max` accept `HH:MM` (24-hour).
              - …
            - object: Local-datetime picker. `min`/`max` accept ISO 8601 timestamps.
              - …
            - object: Native color picker. Submitted value is a 6-digit hex string like `#ff6b35`.
              - …
            - object: Single-select dropdown.
              - …
            - object: Single-select radio group.
              - …
            - object: Multi-select checkbox group when `options` is provided. If `options` is omitted the field renders as a single boolean checkbox.
              - …
        - `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.
              - …
            - `dark` FormColorPalette: Per-mode palette. All three colors are optional; any you omit fall back to the team default for that mode.
              - …
          - `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.
              - …
            - object: Two-stop linear gradient. Supply separate light- and dark-mode gradients; the renderer picks based on `theme_mode`.
              - …
            - object: Image background. The image is tiled or scaled across the page with optional blur and dark tint overlay.
              - …
            - object: Subtle repeating SVG pattern tinted by the palette's foreground color.
              - …
          - `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.
    - `options` SendFormOptions: Optional delivery knobs shared by RCS and SMS sends.
      - `schedule` ScheduleSchema: Define when and how your message should be sent.
        - `sendAt` string, required: The date and time you want your message to be sent. - Use [ISO 8601 format](https://en.wikipedia.org/wiki/ISO_8601), for example: `2024-12-17T14:30:00Z`. Format: `YYYY-MM-DDThh:mm` (e.g., `2024-12-25T14:30`). - This time must be at least 5 minutes in the future. - The message will be scheduled based on the `timezone` you provide. - If you set a `recurrence` schedule, this is the start date and time for the recurring schedule.
        - `recurrence` string: AWS cron expression for recurring schedules (6 fields). <br> [Learn more about cron expressions](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-scheduled-rule-pattern.html). <br> **Examples:** - `0 10 * * ? *` - Every day at 10:00 AM - `0/30 * * * ? *` - Every 30 minutes - `0 9 ? * MON-FRI *` - Every weekday at 9:00 AM - `0 12 1 * ? *` - First day of every month at noon
        - `timezone` string: IANA timezone identifier (e.g., `America/New_York`, `UTC`). <br> Defaults to `UTC` if not specified.
        - `endDate` string: Date and time when recurring messages should stop. <br> Format: `YYYY-MM-DDThh:mm`. Required if `recurrence` is set.
      - `webview_mode` 'FULL' | 'HALF' | 'TALL': RCS webview size for the open-url button action. Ignored for SMS.

## Response `200`

Form sent (or URL minted when `to` is omitted).

- union
  - SendFormResponse: Successful `POST /forms/send` response. - When `to` was provided in the request, the message was dispatched: `submission.to` echoes that recipient and `message_id` is the id of the outbound SMS/RCS that carried the URL. - When `to` was omitted, no message was sent: both `submission.to` and `message_id` are `null` and only the submission URL is returned.
    - `form` Form, required: 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.
              - …
            - `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.
              - …
          - 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.
              - …
            - `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.
              - …
            - `dark` FormGradient, required: A single light- or dark-mode gradient stop pair.
              - …
          - 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.
    - `submission` FormSubmission, required: A single submission against a form.
      - `id` string, required: Submission id (starts with `fsub_`).
      - `url` string, required: Public submission URL (`https://forms.pinnacle.sh/{submission_id}`) — the unguessable credential used by the recipient to fill out the form.
      - `form_id` string, required: Id of the form this submission belongs to (starts with `form_`).
      - `from` string, nullable, required: Sender identifier — E.164 phone number or RCS agent id. Null if the sender could no longer be resolved (e.g. the phone number or agent was deleted after the submission).
      - `to` string, nullable, required: Recipient phone number (E.164). Null for URL-only sends (`to` was omitted at send time).
      - `data` object, nullable, required: Submitted answers keyed by field `key`. Null if the submission has not been completed yet. Each value's shape depends on the field type: - `text`, `email`, `phone`, `url`, `textarea`, `select`, `radio` → string - `number` → number - `checkbox` (single) → boolean - `checkbox` (multi), `multiselect` → array of strings - Skipped optional field → null
      - `ip_address` string, nullable, required: IP address the submission was POSTed from. Null for pending submissions.
      - `user_agent` string, nullable, required: User-Agent header of the browser that submitted the form. Null for pending submissions.
      - `submitted_at` string, date-time, nullable, required: Timestamp of completion. Null for pending submissions; updated on each edit of a `can_update=true` submission.
      - `created_at` string, date-time, required: Timestamp of when the submission URL was minted.
    - `message_id` string, nullable, required: Id of the outbound SMS/RCS message that delivered the URL. Null when the send was URL-only (no `to`).
  - ScheduledFormSendResponse: Response shape for a scheduled form send — returned in place of `SendFormResponse` when `options.schedule` is set.
    - `scheduleId` string, required
    - `config` ScheduleSchema, required: Define when and how your message should be sent.
      - `sendAt` string, required: The date and time you want your message to be sent. - Use [ISO 8601 format](https://en.wikipedia.org/wiki/ISO_8601), for example: `2024-12-17T14:30:00Z`. Format: `YYYY-MM-DDThh:mm` (e.g., `2024-12-25T14:30`). - This time must be at least 5 minutes in the future. - The message will be scheduled based on the `timezone` you provide. - If you set a `recurrence` schedule, this is the start date and time for the recurring schedule.
      - `recurrence` string: AWS cron expression for recurring schedules (6 fields). <br> [Learn more about cron expressions](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-scheduled-rule-pattern.html). <br> **Examples:** - `0 10 * * ? *` - Every day at 10:00 AM - `0/30 * * * ? *` - Every 30 minutes - `0 9 ? * MON-FRI *` - Every weekday at 9:00 AM - `0 12 1 * ? *` - First day of every month at noon
      - `timezone` string: IANA timezone identifier (e.g., `America/New_York`, `UTC`). <br> Defaults to `UTC` if not specified.
      - `endDate` string: Date and time when recurring messages should stop. <br> Format: `YYYY-MM-DDThh:mm`. Required if `recurrence` is set.
    - `form` ScheduledFormSendResponseForm, required
      - `id` string, required
      - `name` string, nullable, required
    - `submission` ScheduledFormSendResponseSubmission, required
      - `id` string, required
      - `url` string, required

## 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.
- `404`: The requested resource could not be found. <br> This may occur if the identifier is incorrect, the resource has been deleted, or you don't have permission to access it.
- `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)
