P
ProFormsDocs

Update Form

Update any part of a form — fields, settings, theme, or metadata. Only include the fields you want to change.

forms.edit
PUT/v1/forms/:id

Request Body

All fields are optional. Only include what you want to change.

namestring

Form name

titlestring

Public-facing title

descriptionstring

Form description

fieldsarray

Complete fields array (replaces existing)

conditionsarray

Conditional logic rules

settingsobject

Form settings (deep-merged — safe to send partial updates)

themeobject | null

Theme overrides (merged with existing; send null to reset)

customCssstring

Custom CSS for the form. Use this top-level field — it is the canonical source and is synced automatically with settings.advanced.customCss.

embedModestring

Embed mode: "isolated" (Shadow DOM) or "open" (inherits site CSS)

siteIdstring

Move form to a different site

⚠️
The fields array is replaced entirely, not merged. Always send the complete fields array when updating fields.
💡
The settings object is deep-merged with the existing settings. You can safely update just settings.thankYou without affecting settings.security, settings.tracking, etc.

Settings Object Reference

The settings object contains all form behavior configuration. Each sub-object is independently mergeable.

settings.thankYou

Controls what happens after form submission — show a message or redirect to a URL.

type*string

"message" to show a thank you page, or "redirect" to send the user to a URL

messagestring

HTML content for the thank you message. Supports merge tags like {name}, {email}, {company}, etc. Only used when type is "message".

redirectUrlstring

URL to redirect to after submission. Only used when type is "redirect".

redirectDelaynumber

Seconds to wait before redirecting (0-10). Default: 0.

showSubmitAnotherboolean

Show a "Submit Another Response" button on the thank you page. Default: false.

iconSizenumber

Size in pixels of the success checkmark icon (32-96). Default: 48.

settings.security

Spam protection and submission limits.

honeypotboolean

Enable honeypot field for bot detection. Default: true.

rateLimitPerHournumber

Max submissions per IP per hour. Default: 10.

recaptchaEnabledboolean

Enable reCAPTCHA v3 for this form. Default: false. The site key used is determined by the site's reCAPTCHA configuration — custom key if set, otherwise the platform global key.

recaptchaThresholdnumber

reCAPTCHA v3 score threshold (0.0–1.0). Submissions scoring below this are rejected. Default: 0.5. Higher values (e.g. 0.7) are stricter; lower values (e.g. 0.3) allow more traffic through.

maxSubmissionsnumber

Total submission limit (0 = unlimited). Default: 0.

maxPerIpnumber

Max submissions per IP total (0 = unlimited). Default: 0.

allowedDomainsstring[]

Restrict form to specific domains (empty = allow all).

recaptchaBadgeModestring

"text" (default) — hides the floating badge and shows a centered attribution line at the bottom of the form. "badge" — shows Google's default floating corner badge. "none" — hides both (you handle compliance).

💡
settings.security.recaptchaEnabled is the canonical reCAPTCHA toggle. The API keeps a flat settings.recaptchaEnabled in sync automatically — you can use either path and the value will always match.

settings.tracking

Analytics and conversion tracking.

gaIdstring

Google Analytics measurement ID (e.g. G-XXXXXXXXXX).

fbPixelIdstring

Facebook Pixel ID.

googleAdsIdstring

Google Ads account ID (e.g. AW-123456789). Loads gtag and fires conversion events on submission.

googleAdsConversionLabelstring

Google Ads conversion label. Required with googleAdsId to fire conversion events.

googleAdsEnhancedConversionsboolean

Send SHA-256 hashed user data (email, phone, name, address) with conversion events. Default: false. Auto-detects email/phone/name/address field types.

headScriptsstring

Custom scripts injected into the form head.

submitScriptsstring

Scripts executed on form submission.

captureUtmboolean

Capture UTM parameters from the page URL. Default: true.

settings.emailNotifications

Email notification configuration for new submissions.

enabledboolean

Enable email notifications. Default: false.

recipientsstring[]

Email addresses to notify.

fromNamestring

Sender display name (e.g. "ProForms").

subjectstring

Email subject line. Supports merge tags like {form_name}, {name}, {site_name}.

bodyHtmlstring

Custom HTML email body. Supports merge tags.

templateStylestring

"html" (default styled template), "plain" (plain text), or "custom" (use bodyHtml).

includeSummaryTableboolean

Include a table of all submitted fields. Default: true.

metadataobject

Toggle submission details in email footer. All default to true.

metadata.showTimestampboolean

Include submission date/time.

metadata.showIpAddressboolean

Include submitter IP address.

metadata.showDeviceboolean

Include parsed device/browser info.

metadata.showReferrerboolean

Include referrer URL.

metadata.showPageUrlboolean

Include form host page URL.

metadata.showUtmParamsboolean

Include UTM parameters (source, medium, campaign, etc).

settings.confirmationEmail

Auto-responder confirmation email sent to the form submitter.

enabledboolean

Enable confirmation email. Default: false.

emailFieldKeystring

The field key of the email field to send the confirmation to.

subjectstring

Email subject line. Supports merge tags. Default: "Thank you for contacting {site_name}".

bodyHtmlstring

Custom HTML body (used when templateStyle is "custom"). Supports merge tags.

templateStylestring

"html" (default thank-you template), "plain" (plain text), or "custom" (use bodyHtml).

replyTostring

Reply-to email address for the confirmation.

includeSummaryboolean

Include a summary of the submission data. Default: true.

attachmentsarray

Static file attachments. Each: { name: string, url: string }.

settings.integrations

Webhook and third-party integrations.

webhooksarray

Array of webhook configurations. Each: { url, method, headers, events, enabled, payloadMode?, payloadTemplate? }.

Webhook Object Properties

enabled*boolean

Whether this webhook is active.

url*string

HTTPS URL to send the webhook to.

method*string

"POST" or "PUT".

headersobject

Custom headers as key-value pairs (e.g. for API authentication).

events*string[]

Array of trigger events: "submission", "update".

payloadModestring

"default" (standard ProForms payload) or "custom" (use payloadTemplate). Default: "default".

payloadTemplatestring

JSON string with {{merge_tags}}. Only used when payloadMode is "custom". See Custom Payload Templates below.

Custom Payload Templates

When payloadMode is "custom", the payloadTemplate string is used as the webhook body instead of the default ProForms payload. Use merge tags to insert form field values, metadata, and system info.

Available Merge Tags
TagDescription
{{field_key}}Value of any form field by its key
{{name.first}}First name (from split name field)
{{name.last}}Last name (from split name field)
{{address.street}}Street address
{{address.city}}City
{{address.state}}State
{{address.zip}}Zip code
{{address.country}}Country
{{_utm.source}}UTM source
{{_utm.campaign}}UTM campaign
{{_utm.medium}}UTM medium
{{_utm.term}}UTM term
{{_utm.content}}UTM content
{{_submission.id}}Submission UUID
{{_submission.timestamp}}Submission ISO timestamp
{{_form.name}}Form name
{{_form.id}}Form UUID
{{_meta.referrer}}Page referrer
{{_meta.pageUrl}}Page URL where form was submitted
{{_meta.userAgent}}User agent string
💡
Dot notation resolves composite field values: {{name.first}} extracts the first name from a split name field, and {{address.city}} extracts the city from an address field. Unresolved tags are replaced with empty strings.
Example: CRM Integration (Fence360)
{
  "vendor": "Techpros",
  "firstName": "{{name.first}}",
  "lastName": "{{name.last}}",
  "emailAddress": "{{email}}",
  "phoneNumber": "{{phone}}",
  "street": "{{address.street}}",
  "city": "{{address.city}}",
  "state": "{{address.state}}",
  "zipCode": "{{address.zip}}",
  "notes": "{{message}}",
  "utmSource": "{{_utm.source}}",
  "utmCampaign": "{{_utm.campaign}}",
  "utmMedium": "{{_utm.medium}}"
}
Example: Generic CRM Push
{
  "contact": {
    "first_name": "{{name.first}}",
    "last_name": "{{name.last}}",
    "email": "{{email}}",
    "phone": "{{phone}}"
  },
  "source": "proforms",
  "form_id": "{{_form.id}}",
  "submitted_at": "{{_submission.timestamp}}",
  "campaign": "{{_utm.campaign}}"
}

settings.advanced

Advanced form configuration.

customCssstring

Additional CSS scoped to this form.

customJsstring

Custom JavaScript executed in the form context.

internalNamestring

Internal name for organization (not shown to users).

descriptionstring

Internal description/notes.

tagsstring[]

Tags for organizing forms.

autoDeleteDaysnumber

Auto-delete submissions after N days (0 = never). Coming soon — not yet active.

Top-level settings

submitButtonTextstring

Submit button label. Default: "Submit".

buttonIconstring

Icon to show on the submit button (e.g. "arrow", "send", "check").

buttonIconPositionstring

"left" or "right". Default: "right".

honeypotEnabledboolean

Legacy honeypot toggle (prefer security.honeypot).

multiStepboolean

Multi-step form mode. **Coming soon** — not yet supported in the embed script.

closedMessagestring

Message shown when form is archived/closed.

settings — Button Styling

Customize the submit button appearance beyond basic text and icon.

buttonHoverColorstring

Button background color on hover (CSS color value).

buttonHoverTextColorstring

Button text color on hover (CSS color value).

buttonBorderboolean

Show border on submit button. Default: false.

buttonBorderColorstring

Border color (CSS color value).

buttonShadowstring

Shadow preset: "none", "sm", "md", "lg". Default: "none".

settings — Disclaimer

Show disclaimer or consent text below the submit button.

disclaimerEnabledboolean

Show disclaimer text below submit button. Default: false.

disclaimerTextstring

Disclaimer text content. Default: "By submitting this form, you agree to our Privacy Policy and Terms of Service."

disclaimerFontSizestring

CSS font size (e.g. "12px", "0.75rem").

disclaimerColorstring

Text color (CSS color value).

disclaimerAlignstring

Text alignment: "left", "center", "right". Default: "center".

Markdown in Labels & Help Text

Field labels, option labels (radio/checkbox), help text, and disclaimer text support inline markdown. Text is stored as-is — markdown is parsed to HTML at render time in both the React renderer and embed.js.

**bold**syntax

Renders as bold text (<strong>).

*italic*syntax

Renders as italic text (<em>).

[text](url)syntax

Renders as a link. Opens in new tab with rel="noopener noreferrer".

~~strikethrough~~syntax

Renders as strikethrough text (<del>).

Example: Set a field label to I agree to the **[Terms](https://example.com/terms)** and it renders as: I agree to the Terms.

Security: Raw HTML tags in input are stripped before markdown is applied. Only safe tags (strong, em, a, del) are generated.

settings — Email Overrides

Override site-level email settings for this specific form. When emailOverride is true, these values take precedence over the site defaults.

emailOverrideboolean

Override site-level email settings for this form. Default: false.

emailFromNamestring

Custom sender name for this form's notifications.

emailReplyTostring

Custom reply-to address.

emailRecipientsstring[]

Custom recipient list (overrides site recipients).

emailCcstring

CC addresses.

emailBccstring

BCC addresses.

Settings Sync (Bi-directional)

Several settings exist in both a nested (structured) path and a flat (legacy) path. The API automatically keeps them in sync — you can write to either path and both will update.

Nested PathFlat PathNotes
settings.thankYou.typesettings.successAction"message" or "redirect"
settings.thankYou.redirectUrlsettings.successConfig.redirectUrlURL for redirect after submission
settings.thankYou.messagesettings.successConfig.bodyThank you message HTML
settings.security.honeypotsettings.honeypotEnabledBot honeypot toggle
settings.security.recaptchaEnabledsettings.recaptchaEnabledreCAPTCHA v3 toggle
settings.security.allowedDomainssettings.allowedDomainsDomain restriction list
settings.security.rateLimitPerHoursettings.rateLimitPerIpRate limit per IP per hour
settings.advanced.customCsscustomCss (top-level)Top-level field is canonical
💡
Use the nested paths (e.g. settings.thankYou, settings.security) for new integrations. The flat paths exist for backward compatibility and the dashboard UI. When both are sent in the same request, the nested path takes priority.

Email Field Validation

Email fields support configurable validation strictness set via the validation object on the field.

validation.emailValidationstring

"standard" (default) — basic format check (x@x.x). "strict" — format check plus blocks common disposable/temporary email domains. "none" — no format validation, accepts any input.

💡
Both client-side (live) and server-side (on submit) validation respect the emailValidation setting — they stay in sync automatically.

Phone Field Validation

Phone fields support auto-formatting, digit-only enforcement, and country-based digit validation. These are set per-field inside the validation object on each field.

validation.phoneFormatstring

"us" — exactly 10 digits (US/Canada, default). "international" — 7–15 digits. "any" — no digit limit.

validation.phoneDisplayFormatstring

"parens" — (480) 555-5555 (default). "dashes" — 480-555-5555. "dots" — 480.555.5555. "none" — 4805555555 (raw digits, no mask).

💡
Auto-formatting kicks in as the user types — digits are masked in real time to the chosen format. Letters are blocked. Paste is sanitized to digits only. The placeholder automatically matches the display format if no custom placeholder is set.
curl — phone field with auto-formatting
curl -X PUT "https://proforms.io/api/v1/forms/f_abc123" \
  -H "Authorization: Bearer pf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": [
      {
        "key": "phone",
        "type": "phone",
        "label": "Phone Number",
        "required": true,
        "width": "full",
        "order": 0,
        "validation": {
          "phoneFormat": "us",
          "phoneDisplayFormat": "parens"
        }
      }
    ]
  }'

Name Field Configuration

Name fields support two formats, controlled by the nameFormat property on the field.

nameFormatstring

"full" (default) — single input for full name. "split" — renders side-by-side First Name and Last Name inputs.

Submission data differs by format:

FormatStored ValueMerge Tags
"full"Plain string: "John Doe"{full_name}
"split"Object: { "first": "John", "last": "Doe" }{first_name}, {last_name}, {full_name}
curl — split name field
curl -X PUT "https://proforms.io/api/v1/forms/f_abc123" \
  -H "Authorization: Bearer pf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": [
      {
        "key": "name",
        "type": "name",
        "label": "Your Name",
        "required": true,
        "nameFormat": "split",
        "width": "full",
        "order": 0
      }
    ]
  }'
💡
Split name fields automatically get autocomplete="given-name" and autocomplete="family-name" for browser autofill and Google Ads Enhanced Conversions. First letters of each word are auto-capitalized as the user types.

Address Field Configuration

Address fields support toggling individual sub-fields via the addressConfig object on the field. Street Address is always shown.

addressConfig.showStreet2boolean

Show Street Address Line 2 (Apt, Suite, etc.). Default: true.

addressConfig.showCityboolean

Show City field. Default: true.

addressConfig.showStateboolean

Show State field. Default: true.

addressConfig.showZipboolean

Show Zip Code field. Default: true.

addressConfig.showCountryboolean

Show Country field. Default: false.

addressConfig.stateModestring

"text" (default) — free text input with auto-capitalize. "us" — searchable dropdown with all 50 US states + DC + territories, shows abbreviations.

addressConfig.stateDefaultstring

Pre-select a state (e.g. "UT", "AZ"). Works with both text and us modes.

addressConfig.stateLockedboolean

Lock the state field so users can't change it. Requires stateDefault. Default: false.

addressConfig.zipFormatstring

"any" (default) — free text. "us" — exactly 5 digits, blocks non-numeric input.

💡
Street, Street 2, City, and Country fields auto-capitalize the first letter of each word as the user types. When stateMode is set to "text", the state field also auto-capitalizes.
curl — US address: state dropdown locked to Utah, 5-digit zip
curl -X PUT "https://proforms.io/api/v1/forms/f_abc123" \
  -H "Authorization: Bearer pf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": [
      {
        "key": "address",
        "type": "address",
        "label": "Service Address",
        "required": true,
        "width": "full",
        "order": 0,
        "addressConfig": {
          "showStreet2": false,
          "showCountry": false,
          "stateMode": "us",
          "stateDefault": "UT",
          "stateLocked": true,
          "zipFormat": "us"
        }
      }
    ]
  }'
💡
Hidden sub-fields are not rendered in the embed and won't appear in submission data. The row layout adjusts automatically — if only one field in a row is visible, it takes full width instead of 50%.

Conditional Logic

Show, hide, require, or set values on fields based on other fields' values. Conditions are evaluated client-side by the embed script.

Top-Level Conditions Schema

Each condition has a logic operator, rules to evaluate, and actions to perform when rules match.

logicstring

"AND" or "OR" (also accepts "all"/"any"). Default: "AND". Determines whether all rules or any rule must match.

rules*array

Array of rule objects to evaluate.

rules[].fieldKey*string

The key of the field to check.

rules[].operator*string

"equals", "not_equals", "contains", "not_contains", "is_empty", "is_not_empty", "greater_than", "less_than".

rules[].valuestring

Value to compare against. Optional for is_empty/is_not_empty operators.

actions*array

Array of actions to execute when condition matches.

actions[].type*string

"show", "hide", "require", "unrequire", or "set_value".

actions[].targetFieldKey*string

The key of the field to act on.

actions[].valuestring

Value to set (only used with "set_value" action).

Field-Level Conditions (Shorthand)

For convenience, you can attach conditions directly to a field. The API automatically converts them to top-level format before saving.

conditions.action*string

"show" or "hide" — what to do with this field when rules match.

conditions.logicstring

"all" or "any" (also "AND"/"OR"). Default: "all".

conditions.rules*array

Array of rule objects.

conditions.rules[].field*string

The key of the field to check.

conditions.rules[].operator*string

Comparison operator (same options as top-level).

conditions.rules[].valuestring

Value to compare against.

💡
Field-level conditions are stripped from the stored fields and merged into the top-level conditions array automatically. The embed script only reads top-level conditions.
curl — field-level conditions shorthand
curl -X PUT "https://proforms.io/api/v1/forms/f_abc123" \
  -H "Authorization: Bearer pf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": [
      {
        "key": "service_type",
        "type": "radio",
        "label": "Service Type",
        "required": true,
        "width": "full",
        "options": [
          {"label": "Residential", "value": "residential"},
          {"label": "Commercial", "value": "commercial"}
        ]
      },
      {
        "key": "company",
        "type": "text",
        "label": "Company Name",
        "required": false,
        "width": "full",
        "conditions": {
          "action": "show",
          "logic": "all",
          "rules": [
            {"field": "service_type", "operator": "equals", "value": "commercial"}
          ]
        }
      }
    ]
  }'
curl — top-level conditions (explicit)
curl -X PUT "https://proforms.io/api/v1/forms/f_abc123" \
  -H "Authorization: Bearer pf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "conditions": [
      {
        "logic": "AND",
        "rules": [
          {"fieldKey": "budget", "operator": "greater_than", "value": "10000"}
        ],
        "actions": [
          {"type": "show", "targetFieldKey": "enterprise_details"},
          {"type": "require", "targetFieldKey": "enterprise_details"}
        ]
      }
    ]
  }'
⚠️
If a condition references a field key that doesn't exist in the form, the API will still save successfully but return a warnings array in the response.

Field Ordering

The order property on each field is optional. If omitted, fields are auto-assigned order values based on their position in the array (0, 1, 2, ...). If duplicate order values are detected, all orders are re-normalized based on array position.

💡
You can omit order entirely when creating forms via the API — the array order determines display order.

Font Size Controls

Font sizes can be set at two levels: global (theme) and per-field (override). Per-field values take priority over theme values, which take priority over the default of 15px.

theme.fontSizestring

Global input/option text font size (e.g. "15px"). Default: "15px".

theme.labelFontSizestring

Global label/title font size (e.g. "15px"). Default: "15px".

fields[].fontSizenumber

Per-field input font size override in px (10-20). Omit to inherit from theme.

fields[].labelFontSizenumber

Per-field label font size override in px (10-20). Omit to inherit from theme.

💡
Use per-field overrides for elements that need different sizing — like consent disclaimers at 13px while the rest of the form stays at 15px.

Examples

Set redirect after submission

curl
curl -X PUT "https://proforms.io/api/v1/forms/f_abc123" \
  -H "Authorization: Bearer pf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "settings": {
      "thankYou": {
        "type": "redirect",
        "redirectUrl": "https://example.com/thank-you",
        "redirectDelay": 3
      }
    }
  }'

Custom thank you message with merge tags

curl
curl -X PUT "https://proforms.io/api/v1/forms/f_abc123" \
  -H "Authorization: Bearer pf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "settings": {
      "thankYou": {
        "type": "message",
        "message": "<h2>Thanks, {name}!</h2><p>We received your request and will reach out to <strong>{email}</strong> within 24 hours.</p>",
        "showSubmitAnother": true,
        "iconSize": 64
      }
    }
  }'

Update embed mode and button text

curl
curl -X PUT "https://proforms.io/api/v1/forms/f_abc123" \
  -H "Authorization: Bearer pf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "embedMode": "open",
    "settings": {
      "submitButtonText": "Get My Free Quote"
    }
  }'

Configure security settings

curl
curl -X PUT "https://proforms.io/api/v1/forms/f_abc123" \
  -H "Authorization: Bearer pf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "settings": {
      "security": {
        "rateLimitPerHour": 20,
        "maxSubmissions": 500,
        "allowedDomains": ["example.com", "app.example.com"]
      }
    }
  }'

Enable reCAPTCHA with custom threshold

curl
curl -X PUT "https://proforms.io/api/v1/forms/f_abc123" \
  -H "Authorization: Bearer pf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "settings": {
      "security": {
        "recaptchaEnabled": true,
        "recaptchaThreshold": 0.7
      }
    }
  }'

The reCAPTCHA site key is resolved automatically — uses the site's custom key if configured, otherwise falls back to the ProForms platform key. The threshold (0.0–1.0) determines how strict scoring is; 0.5 is Google's recommended default.

Disable reCAPTCHA

curl
curl -X PUT "https://proforms.io/api/v1/forms/f_abc123" \
  -H "Authorization: Bearer pf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "settings": {
      "security": {
        "recaptchaEnabled": false
      }
    }
  }'

Apply custom CSS

curl
curl -X PUT "https://proforms.io/api/v1/forms/f_abc123" \
  -H "Authorization: Bearer pf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "customCss": ".pf-form { border-radius: 16px; } .pf-submit { background: #1a1a1a; }"
  }'

Response

200 OK
{
  "success": true,
  "data": {
    "id": "f_abc123",
    "name": "Updated Contact Form",
    "version": 4,
    "updatedAt": "2026-02-17T11:00:00.000Z"
  }
}

If conditions reference non-existent field keys, the response includes a warnings array:

200 OK (with warnings)
{
  "success": true,
  "data": {
    "id": "f_abc123",
    "name": "Updated Contact Form",
    "version": 4,
    "updatedAt": "2026-02-17T11:00:00.000Z",
    "warnings": [
      "Condition references field 'foo' which doesn't exist in this form"
    ]
  }
}
💡
The version number increments with every update, which is useful for conflict detection.