> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.pinnacle.sh/v-2/api-reference/forms/send-form/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.pinnacle.sh/_mcp/server. # Send Form POST https://api.pinnacle.sh/forms/send Content-Type: application/json 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 Messages and User Events](/guides/messages/receiving). Reference: https://docs.pinnacle.sh/api-reference/forms/send-form ## Authentication - `PINNACLE-API-KEY` header (required) — API Key authentication via header ## Request ### Body (application/json) This endpoint expects a SendFormRequest. - `SendFormRequest` ## Response ### 200 Form sent (or URL minted when `to` is omitted). - `forms_send_Response_200` ## Errors ### 400 Bad Request Error Validation failed. The payload has missing required fields and/or invalid types. See [https://zod.dev/error-formatting](https://zod.dev/error-formatting) for more information. - `description` (string, required) — Human-readable summary of validation failures. - `errors` (ZodErrorErrors, required) — Structured dictionary of issues containing two main sections: - `errors`: Array of global validation errors not tied to specific fields - `properties`: Object mapping field names to their specific validation errors, where each field contains an `errors` array ### 401 Unauthorized Error The request lacks valid authentication credentials or the provided credentials are invalid. Ensure you're including a valid API key in the request headers and that your account has the necessary permissions to access this endpoint. - `error` (string, required) — Human-readable description of the error that occurred, corresponding to the HTTP status code. ### 404 Not Found Error The requested resource could not be found. This may occur if the identifier is incorrect, the resource has been deleted, or you don't have permission to access it. - `error` (string, required) — Human-readable description of the error that occurred, corresponding to the HTTP status code. ### 500 Internal Server Error An unexpected error occurred on Pinnacle's servers while processing your request. If this error persists, please contact support with the request details. - `error` (string, required) — Human-readable description of the error that occurred, corresponding to the HTTP status code. ## Types ### SendFormViaRcsRequest Send a form from an RCS agent. `from` must be an `agent_*` id. - `from` (string, required) — RCS agent id (`agent_*`). - `form` (SendFormViaRcsRequestForm, required) — Existing form id or an inline definition that mints a new form for this send. - `to` (string, optional) — 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`. - `fallback` (SendFormViaRcsRequestFallback, optional) — When RCS is unavailable on the recipient's device, fall back to SMS from this number. Requires `to`. - `options` (SendFormOptions, optional) — Optional delivery knobs shared by RCS and SMS sends. ### SendFormViaSmsRequest Send a form from a phone number. `from` must be an E.164 phone. - `from` (string, required) — Sender phone in E.164. - `form` (SendFormViaSmsRequestForm, required) - `to` (string, optional) — Recipient phone in E.164. Omit to mint a URL-only form without sending a message. - `options` (SendFormOptions, optional) — Optional delivery knobs shared by RCS and SMS sends. ### 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. - `submission` (FormSubmission, required) — A single submission against a form. - `message_id` (string, required, nullable) — 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. - `form` (ScheduledFormSendResponseForm, required) - `submission` (ScheduledFormSendResponseSubmission, required) ### ZodErrorErrors Structured dictionary of issues containing two main sections: - `errors`: Array of global validation errors not tied to specific fields - `properties`: Object mapping field names to their specific validation errors, where each field contains an `errors` array ### SendFormViaRcsRequestForm Existing form id or an inline definition that mints a new form for this send. ### 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. ### SendFormOptions Optional delivery knobs shared by RCS and SMS sends. - `schedule` (ScheduleSchema, optional) — Define when and how your message should be sent. - `webview_mode` (enum, optional, default: FULL) — RCS webview size for the open-url button action. Ignored for SMS. - Allowed values: `FULL`, `HALF`, `TALL` ### SendFormViaSmsRequestForm ### 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, required, nullable) — Human-readable name for the form. Rendered as the form's title. - `description` (string, required, nullable) — Longer description rendered below the title. - `fields` (list of FormField, required) — Ordered list of fields the recipient fills out. - `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, required, nullable) — After this timestamp the form stops accepting submissions. Null means no expiration. - `theme_override` (FormThemeOverride, required, nullable) — Per-form theme tweaks layered on top of your team's default theme. Null means the team defaults are used as-is. - `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, required, nullable) — 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, required, nullable) — 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, required) — ISO 8601 timestamp of when the form was created. - `updated_at` (string, required) — ISO 8601 timestamp of the most recent form mutation. ### FormSubmission 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, required, nullable) — 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, required, nullable) — Recipient phone number (E.164). Null for URL-only sends (`to` was omitted at send time). - `data` (map from string to any, required, nullable) — 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, required, nullable) — IP address the submission was POSTed from. Null for pending submissions. - `user_agent` (string, required, nullable) — User-Agent header of the browser that submitted the form. Null for pending submissions. - `submitted_at` (string, required, nullable) — Timestamp of completion. Null for pending submissions; updated on each edit of a `can_update=true` submission. - `created_at` (string, required) — Timestamp of when the submission URL was minted. ### 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, optional) — AWS cron expression for recurring schedules (6 fields). [Learn more about cron expressions](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-scheduled-rule-pattern.html). **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, optional, default: UTC) — IANA timezone identifier (e.g., `America/New_York`, `UTC`). Defaults to `UTC` if not specified. - `endDate` (string, optional) — Date and time when recurring messages should stop. Format: `YYYY-MM-DDThh:mm`. Required if `recurrence` is set. ### ScheduledFormSendResponseForm - `id` (string, required) - `name` (string, required, nullable) ### ScheduledFormSendResponseSubmission - `id` (string, required) - `url` (string, required) ### SendFormViaRcsRequestForm1 The minimum shape needed to create a form. Used by `POST /forms` and inline by `POST /forms/send`. - `fields` (list of FormField, required) — Ordered list of fields the recipient will fill out. At least one field is required. - `name` (string, optional) — Human-readable name for the form. Rendered as the form's title. - `description` (string, optional) — Longer description rendered below the title. - `can_update` (boolean, optional, default: false) — 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, optional) — After this timestamp the form stops accepting submissions. Omit for no expiration. - `theme_override` (FormThemeOverride, optional, nullable) — Per-form theme tweaks layered on top of your team's default theme. Omit or set to null to use team defaults. ### SendFormViaSmsRequestForm1 The minimum shape needed to create a form. Used by `POST /forms` and inline by `POST /forms/send`. - `fields` (list of FormField, required) — Ordered list of fields the recipient will fill out. At least one field is required. - `name` (string, optional) — Human-readable name for the form. Rendered as the form's title. - `description` (string, optional) — Longer description rendered below the title. - `can_update` (boolean, optional, default: false) — 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, optional) — After this timestamp the form stops accepting submissions. Omit for no expiration. - `theme_override` (FormThemeOverride, optional, nullable) — Per-form theme tweaks layered on top of your team's default theme. Omit or set to null to use team defaults. ### FormField A single field definition inside a form. Discriminated by `type`. - `type`: `text` (text) - `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. - `help_text` (string, optional) — Small caption rendered under the input to give the submitter extra context. - `max_length` (integer, optional) — Maximum number of characters the submitter may enter. - `min_length` (integer, optional) — Minimum number of characters the submitter must enter. - `pattern` (string, optional) — Regex the value must match. Validated server-side with RE2, so patterns that enable catastrophic backtracking (backreferences, lookarounds) are rejected at form creation. - `placeholder` (string, optional) — Ghost text shown inside the input while it's empty. - `required` (boolean, optional, default: false) — When true, the submitter must provide a value. Default false. - `type`: `textarea` (textarea) - `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. - `help_text` (string, optional) — Small caption rendered under the input to give the submitter extra context. - `max_length` (integer, optional) — Maximum number of characters the submitter may enter. - `min_length` (integer, optional) — Minimum number of characters the submitter must enter. - `pattern` (string, optional) — Regex the value must match. Same RE2 safety check as `TextField.pattern`. - `placeholder` (string, optional) — Ghost text shown inside the input while it's empty. - `required` (boolean, optional, default: false) — When true, the submitter must provide a value. Default false. - `type`: `address` (address) - `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. - `help_text` (string, optional) — Small caption rendered under the input to give the submitter extra context. - `max_length` (integer, optional) — Maximum number of characters in the selected address. - `min_length` (integer, optional) — Minimum number of characters in the selected address. - `pattern` (string, optional) — Regex the value must match. Same RE2 safety check as `TextField.pattern`. Rarely needed for addresses — usually left off. - `placeholder` (string, optional) — Ghost text shown inside the input while it's empty. - `required` (boolean, optional, default: false) — When true, the submitter must provide a value. Default false. - `type`: `email` (email) - `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. - `help_text` (string, optional) — Small caption rendered under the input to give the submitter extra context. - `placeholder` (string, optional) — Ghost text shown inside the input while it's empty. - `required` (boolean, optional, default: false) — When true, the submitter must provide a value. Default false. - `type`: `url` (url) - `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. - `help_text` (string, optional) — Small caption rendered under the input to give the submitter extra context. - `placeholder` (string, optional) — Ghost text shown inside the input while it's empty. - `required` (boolean, optional, default: false) — When true, the submitter must provide a value. Default false. - `type`: `phone` (phone) - `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. - `help_text` (string, optional) — Small caption rendered under the input to give the submitter extra context. - `placeholder` (string, optional) — Ghost text shown inside the input while it's empty. - `required` (boolean, optional, default: false) — When true, the submitter must provide a value. Default false. - `type`: `number` (number) - `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. - `help_text` (string, optional) — Small caption rendered under the input to give the submitter extra context. - `max` (double, optional) — Maximum accepted value. - `min` (double, optional) — Minimum accepted value. - `placeholder` (string, optional) — Ghost text shown inside the input while it's empty. - `required` (boolean, optional, default: false) — When true, the submitter must provide a value. Default false. - `step` (double, optional) — Increment between valid values. For example, `step: 0.5` lets the user enter 1, 1.5, 2, etc. - `type`: `range` (range) - `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. - `help_text` (string, optional) — Small caption rendered under the input to give the submitter extra context. - `max` (double, optional, default: 100) — Upper bound of the slider. - `min` (double, optional, default: 0) — Lower bound of the slider. - `required` (boolean, optional, default: false) — When true, the submitter must provide a value. Default false. - `step` (double, optional) — Increment between adjacent slider stops. - `type`: `rating` (rating) - `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. - `help_text` (string, optional) — Small caption rendered under the input to give the submitter extra context. - `max` (integer, optional, default: 5) — Number of stars rendered. - `min` (integer, optional, default: 0) — Minimum allowed rating. `0` means the submitter can clear their selection. - `required` (boolean, optional, default: false) — When true, the submitter must provide a value. Default false. - `type`: `date` (date) - `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. - `help_text` (string, optional) — Small caption rendered under the input to give the submitter extra context. - `max` (string, optional) — Latest selectable date as `YYYY-MM-DD`. - `min` (string, optional) — Earliest selectable date as `YYYY-MM-DD`. - `placeholder` (string, optional) — Ghost text shown inside the input while it's empty. - `required` (boolean, optional, default: false) — When true, the submitter must provide a value. Default false. - `type`: `time` (time) - `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. - `help_text` (string, optional) — Small caption rendered under the input to give the submitter extra context. - `max` (string, optional) — Latest selectable time as `HH:MM` (24-hour). - `min` (string, optional) — Earliest selectable time as `HH:MM` (24-hour). - `placeholder` (string, optional) — Ghost text shown inside the input while it's empty. - `required` (boolean, optional, default: false) — When true, the submitter must provide a value. Default false. - `type`: `datetime` (datetime) - `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. - `help_text` (string, optional) — Small caption rendered under the input to give the submitter extra context. - `max` (string, optional) — Latest selectable instant as an ISO 8601 timestamp. - `min` (string, optional) — Earliest selectable instant as an ISO 8601 timestamp. - `placeholder` (string, optional) — Ghost text shown inside the input while it's empty. - `required` (boolean, optional, default: false) — When true, the submitter must provide a value. Default false. - `type`: `color` (color) - `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. - `help_text` (string, optional) — Small caption rendered under the input to give the submitter extra context. - `required` (boolean, optional, default: false) — When true, the submitter must provide a value. Default false. - `type`: `select` (select) - `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. - `options` (list of FormFieldOption, required) — Choices shown in the dropdown. At least one is required. - `help_text` (string, optional) — Small caption rendered under the input to give the submitter extra context. - `placeholder` (string, optional) — Placeholder row shown when no option is selected (e.g. "Select…"). - `required` (boolean, optional, default: false) — When true, the submitter must provide a value. Default false. - `type`: `radio` (radio) - `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. - `options` (list of FormFieldOption, required) — Choices shown as radio buttons. At least one is required. - `help_text` (string, optional) — Small caption rendered under the input to give the submitter extra context. - `required` (boolean, optional, default: false) — When true, the submitter must provide a value. Default false. - `type`: `checkbox` (checkbox) - `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. - `help_text` (string, optional) — Small caption rendered under the input to give the submitter extra context. - `max_selected` (integer, optional) — Maximum number of boxes the submitter may check. Extra boxes become uninteractive once the cap is reached. - `min_selected` (integer, optional) — Minimum number of boxes the submitter must check. - `options` (list of FormFieldOption, optional) — Choices shown as checkboxes. Omit to render a single boolean checkbox. - `required` (boolean, optional, default: false) — When true, the submitter must provide a value. Default false. ### 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, optional, 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, optional, nullable) — Favicon shown in the browser tab when the form page loads. - `theme_mode` (enum, optional) — Rendering mode. `light` forces the light palette, `dark` forces dark, and `auto` follows the respondent's OS-level appearance setting. - Allowed values: `light`, `dark`, `auto` - `colors` (FormThemeOverrideColors, optional) — 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. - `font_family` (enum, optional) — Typeface stack. `system` uses the respondent's OS default sans-serif. - Allowed values: `inter`, `system`, `serif`, `mono`, `rounded` - `corner_radius` (enum, optional) — Border-radius preset for cards, inputs, and buttons. - Allowed values: `sharp`, `rounded`, `pill` - `content_alignment` (enum, optional) — Horizontal alignment for the form header (title, description, logo). - Allowed values: `left`, `center`, `right` - `background` (FormBackground, optional) — Background layer for the page. Choose solid, gradient, image, or a built-in pattern. - `submit_button_label` (string, optional) — Text shown on the submit button. Defaults to `Submit`. - `success_message` (string, optional) — 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, optional, nullable) — When set, the submitter is redirected here after a successful submit instead of seeing the success panel. - `privacy_url` (string, optional, nullable) — Link target for the `Privacy` footer link. Omit or set to null to hide the link. - `terms_url` (string, optional, nullable) — Link target for the `Terms` footer link. Omit or set to null to hide the link. - `og_image_url` (string, optional, nullable) — Image shown when the form URL is shared in social / messaging link previews (Open Graph). - `og_description` (string, optional, nullable) — Open Graph description used in link previews. Falls back to the form's `description` when unset. ### FormFieldOption A single choice inside a `select`, `radio`, or `checkbox` field. - `value` (string, required) — Machine value sent back in the submission payload. - `label` (string, required) — Human label rendered in the form. ### 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, optional) — Palette used when the form renders in light mode. - `dark` (FormColorPalette, optional) — Palette used when the form renders in dark mode. ### FormBackground Background layer for the form page. Discriminated by `type`. - `type`: `solid` (solid) - `type`: `gradient` (gradient) - `dark` (FormGradient, required) — Gradient used when the form renders in dark mode. - `light` (FormGradient, required) — Gradient used when the form renders in light mode. - `type`: `image` (image) - `blur` (boolean, required) — When true, blurs the background image. - `tint_opacity` (integer, required) — Darkening overlay intensity from 0 (no overlay) to 80 (heavy). Helps preserve text contrast over busy images. - `url` (string, required) — HTTPS URL of the background image. Must start with `https://`. - `zoom` (integer, required) — Scale percentage. 100 is native size; higher values crop the image tighter. - `type`: `pattern` (pattern) - `preset` (enum, required) — Which built-in pattern to render. - Allowed values: `noise`, `dots`, `mesh`, `grid`, `waves`, `topo` ### FormColorPalette Per-mode palette. All three colors are optional; any you omit fall back to the team default for that mode. - `primary` (string, optional) — Accent color used for the submit button, focus rings, filled checkboxes, and other interactive surfaces. - `background` (string, optional) — Page background color. Fallback solid color used by Safari chrome tinting and safe-area regions. - `text` (string, optional) — Body text and label color. ### FormGradient 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` (enum, required) — Direction the gradient runs. `top-bottom` is vertical; `diagonal` runs top-left → bottom-right. - Allowed values: `top-bottom`, `diagonal` ## Examples ### RCS send with recipient **Request** ```json { "from": "agent_iM9wQcyBBjYn", "to": "+14155551234", "form": "form_Oy2n7iUoi9CJwUU6", "fallback": { "from": "+14155550000" }, "options": { "webview_mode": "FULL" } } ``` **Response** ```json { "form": { "id": "form_abc123", "url": "https://forms.pinnacle.sh/form_abc123", "name": "Contact request", "description": null, "fields": [ { "key": "full_name", "label": "Full name", "required": true, "type": "text" } ], "can_update": false, "expires_at": null, "theme_override": null, "submission_count": 0, "last_submitted_at": null, "archived_at": null, "created_at": "2026-04-24T00:00:00Z", "updated_at": "2026-04-24T00:00:00Z" }, "submission": { "id": "fsub_xyz789", "url": "https://forms.pinnacle.sh/fsub_xyz789", "form_id": "form_abc123", "from": "agent_iM9wQcyBBjYn", "to": "+14155551234", "data": null, "ip_address": null, "user_agent": null, "submitted_at": null, "created_at": "2026-04-24T00:00:00Z" }, "message_id": "msg_123" } ``` **SDK Code** ```python RCS send with recipient import requests url = "https://api.pinnacle.sh/forms/send" payload = { "from": "agent_iM9wQcyBBjYn", "to": "+14155551234", "form": "form_Oy2n7iUoi9CJwUU6", "fallback": { "from": "+14155550000" }, "options": { "webview_mode": "FULL" } } headers = { "PINNACLE-API-KEY": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript RCS send with recipient const url = 'https://api.pinnacle.sh/forms/send'; const options = { method: 'POST', headers: {'PINNACLE-API-KEY': '', 'Content-Type': 'application/json'}, body: '{"from":"agent_iM9wQcyBBjYn","to":"+14155551234","form":"form_Oy2n7iUoi9CJwUU6","fallback":{"from":"+14155550000"},"options":{"webview_mode":"FULL"}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go RCS send with recipient package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.pinnacle.sh/forms/send" payload := strings.NewReader("{\n \"from\": \"agent_iM9wQcyBBjYn\",\n \"to\": \"+14155551234\",\n \"form\": \"form_Oy2n7iUoi9CJwUU6\",\n \"fallback\": {\n \"from\": \"+14155550000\"\n },\n \"options\": {\n \"webview_mode\": \"FULL\"\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("PINNACLE-API-KEY", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby RCS send with recipient require 'uri' require 'net/http' url = URI("https://api.pinnacle.sh/forms/send") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["PINNACLE-API-KEY"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"from\": \"agent_iM9wQcyBBjYn\",\n \"to\": \"+14155551234\",\n \"form\": \"form_Oy2n7iUoi9CJwUU6\",\n \"fallback\": {\n \"from\": \"+14155550000\"\n },\n \"options\": {\n \"webview_mode\": \"FULL\"\n }\n}" response = http.request(request) puts response.read_body ``` ```java RCS send with recipient import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.pinnacle.sh/forms/send") .header("PINNACLE-API-KEY", "") .header("Content-Type", "application/json") .body("{\n \"from\": \"agent_iM9wQcyBBjYn\",\n \"to\": \"+14155551234\",\n \"form\": \"form_Oy2n7iUoi9CJwUU6\",\n \"fallback\": {\n \"from\": \"+14155550000\"\n },\n \"options\": {\n \"webview_mode\": \"FULL\"\n }\n}") .asString(); ``` ```php RCS send with recipient request('POST', 'https://api.pinnacle.sh/forms/send', [ 'body' => '{ "from": "agent_iM9wQcyBBjYn", "to": "+14155551234", "form": "form_Oy2n7iUoi9CJwUU6", "fallback": { "from": "+14155550000" }, "options": { "webview_mode": "FULL" } }', 'headers' => [ 'Content-Type' => 'application/json', 'PINNACLE-API-KEY' => '', ], ]); echo $response->getBody(); ``` ```csharp RCS send with recipient using RestSharp; var client = new RestClient("https://api.pinnacle.sh/forms/send"); var request = new RestRequest(Method.POST); request.AddHeader("PINNACLE-API-KEY", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"from\": \"agent_iM9wQcyBBjYn\",\n \"to\": \"+14155551234\",\n \"form\": \"form_Oy2n7iUoi9CJwUU6\",\n \"fallback\": {\n \"from\": \"+14155550000\"\n },\n \"options\": {\n \"webview_mode\": \"FULL\"\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift RCS send with recipient import Foundation let headers = [ "PINNACLE-API-KEY": "", "Content-Type": "application/json" ] let parameters = [ "from": "agent_iM9wQcyBBjYn", "to": "+14155551234", "form": "form_Oy2n7iUoi9CJwUU6", "fallback": ["from": "+14155550000"], "options": ["webview_mode": "FULL"] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.pinnacle.sh/forms/send")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### URL-only mint (no recipient) **Request** ```json { "from": "agent_iM9wQcyBBjYn", "to": "+14155551234", "form": "form_Oy2n7iUoi9CJwUU6", "fallback": { "from": "+14155550000" }, "options": { "webview_mode": "FULL" } } ``` **Response** ```json { "form": { "id": "form_abc123", "url": "https://forms.pinnacle.sh/form_abc123", "name": "Contact request", "description": null, "fields": [ { "key": "full_name", "label": "Full name", "required": true, "type": "text" } ], "can_update": false, "expires_at": null, "theme_override": null, "submission_count": 0, "last_submitted_at": null, "archived_at": null, "created_at": "2026-04-24T00:00:00Z", "updated_at": "2026-04-24T00:00:00Z" }, "submission": { "id": "fsub_xyz789", "url": "https://forms.pinnacle.sh/fsub_xyz789", "form_id": "form_abc123", "from": "+14155550000", "to": null, "data": null, "ip_address": null, "user_agent": null, "submitted_at": null, "created_at": "2026-04-24T00:00:00Z" }, "message_id": null } ``` **SDK Code** ```python URL-only mint (no recipient) import requests url = "https://api.pinnacle.sh/forms/send" payload = { "from": "agent_iM9wQcyBBjYn", "to": "+14155551234", "form": "form_Oy2n7iUoi9CJwUU6", "fallback": { "from": "+14155550000" }, "options": { "webview_mode": "FULL" } } headers = { "PINNACLE-API-KEY": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript URL-only mint (no recipient) const url = 'https://api.pinnacle.sh/forms/send'; const options = { method: 'POST', headers: {'PINNACLE-API-KEY': '', 'Content-Type': 'application/json'}, body: '{"from":"agent_iM9wQcyBBjYn","to":"+14155551234","form":"form_Oy2n7iUoi9CJwUU6","fallback":{"from":"+14155550000"},"options":{"webview_mode":"FULL"}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go URL-only mint (no recipient) package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.pinnacle.sh/forms/send" payload := strings.NewReader("{\n \"from\": \"agent_iM9wQcyBBjYn\",\n \"to\": \"+14155551234\",\n \"form\": \"form_Oy2n7iUoi9CJwUU6\",\n \"fallback\": {\n \"from\": \"+14155550000\"\n },\n \"options\": {\n \"webview_mode\": \"FULL\"\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("PINNACLE-API-KEY", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby URL-only mint (no recipient) require 'uri' require 'net/http' url = URI("https://api.pinnacle.sh/forms/send") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["PINNACLE-API-KEY"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"from\": \"agent_iM9wQcyBBjYn\",\n \"to\": \"+14155551234\",\n \"form\": \"form_Oy2n7iUoi9CJwUU6\",\n \"fallback\": {\n \"from\": \"+14155550000\"\n },\n \"options\": {\n \"webview_mode\": \"FULL\"\n }\n}" response = http.request(request) puts response.read_body ``` ```java URL-only mint (no recipient) import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.pinnacle.sh/forms/send") .header("PINNACLE-API-KEY", "") .header("Content-Type", "application/json") .body("{\n \"from\": \"agent_iM9wQcyBBjYn\",\n \"to\": \"+14155551234\",\n \"form\": \"form_Oy2n7iUoi9CJwUU6\",\n \"fallback\": {\n \"from\": \"+14155550000\"\n },\n \"options\": {\n \"webview_mode\": \"FULL\"\n }\n}") .asString(); ``` ```php URL-only mint (no recipient) request('POST', 'https://api.pinnacle.sh/forms/send', [ 'body' => '{ "from": "agent_iM9wQcyBBjYn", "to": "+14155551234", "form": "form_Oy2n7iUoi9CJwUU6", "fallback": { "from": "+14155550000" }, "options": { "webview_mode": "FULL" } }', 'headers' => [ 'Content-Type' => 'application/json', 'PINNACLE-API-KEY' => '', ], ]); echo $response->getBody(); ``` ```csharp URL-only mint (no recipient) using RestSharp; var client = new RestClient("https://api.pinnacle.sh/forms/send"); var request = new RestRequest(Method.POST); request.AddHeader("PINNACLE-API-KEY", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"from\": \"agent_iM9wQcyBBjYn\",\n \"to\": \"+14155551234\",\n \"form\": \"form_Oy2n7iUoi9CJwUU6\",\n \"fallback\": {\n \"from\": \"+14155550000\"\n },\n \"options\": {\n \"webview_mode\": \"FULL\"\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift URL-only mint (no recipient) import Foundation let headers = [ "PINNACLE-API-KEY": "", "Content-Type": "application/json" ] let parameters = [ "from": "agent_iM9wQcyBBjYn", "to": "+14155551234", "form": "form_Oy2n7iUoi9CJwUU6", "fallback": ["from": "+14155550000"], "options": ["webview_mode": "FULL"] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.pinnacle.sh/forms/send")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` > One API for RCS, iMessage, MMS, and SMS. Build, test, and scale every channel — send your first message in minutes, not weeks.