Update Form
Update any part of a form — fields, settings, theme, or metadata. Only include the fields you want to change.
/v1/forms/:idRequest Body
All fields are optional. Only include what you want to change.
namestringForm name
titlestringPublic-facing title
descriptionstringForm description
fieldsarrayComplete fields array (replaces existing)
conditionsarrayConditional logic rules
settingsobjectForm settings (deep-merged — safe to send partial updates)
themeobject | nullTheme overrides (merged with existing; send null to reset)
customCssstringCustom CSS for the form. Use this top-level field — it is the canonical source and is synced automatically with settings.advanced.customCss.
embedModestringEmbed mode: "isolated" (Shadow DOM) or "open" (inherits site CSS)
siteIdstringMove form to a different site
fields array is replaced entirely, not merged. Always send the complete fields array when updating fields.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
messagestringHTML content for the thank you message. Supports merge tags like {name}, {email}, {company}, etc. Only used when type is "message".
redirectUrlstringURL to redirect to after submission. Only used when type is "redirect".
redirectDelaynumberSeconds to wait before redirecting (0-10). Default: 0.
showSubmitAnotherbooleanShow a "Submit Another Response" button on the thank you page. Default: false.
iconSizenumberSize in pixels of the success checkmark icon (32-96). Default: 48.
settings.security
Spam protection and submission limits.
honeypotbooleanEnable honeypot field for bot detection. Default: true.
rateLimitPerHournumberMax submissions per IP per hour. Default: 10.
recaptchaEnabledbooleanEnable 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.
recaptchaThresholdnumberreCAPTCHA 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.
maxSubmissionsnumberTotal submission limit (0 = unlimited). Default: 0.
maxPerIpnumberMax 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.
gaIdstringGoogle Analytics measurement ID (e.g. G-XXXXXXXXXX).
fbPixelIdstringFacebook Pixel ID.
googleAdsIdstringGoogle Ads account ID (e.g. AW-123456789). Loads gtag and fires conversion events on submission.
googleAdsConversionLabelstringGoogle Ads conversion label. Required with googleAdsId to fire conversion events.
googleAdsEnhancedConversionsbooleanSend SHA-256 hashed user data (email, phone, name, address) with conversion events. Default: false. Auto-detects email/phone/name/address field types.
headScriptsstringCustom scripts injected into the form head.
submitScriptsstringScripts executed on form submission.
captureUtmbooleanCapture UTM parameters from the page URL. Default: true.
settings.emailNotifications
Email notification configuration for new submissions.
enabledbooleanEnable email notifications. Default: false.
recipientsstring[]Email addresses to notify.
fromNamestringSender display name (e.g. "ProForms").
subjectstringEmail subject line. Supports merge tags like {form_name}, {name}, {site_name}.
bodyHtmlstringCustom HTML email body. Supports merge tags.
templateStylestring"html" (default styled template), "plain" (plain text), or "custom" (use bodyHtml).
includeSummaryTablebooleanInclude a table of all submitted fields. Default: true.
metadataobjectToggle submission details in email footer. All default to true.
metadata.showTimestampbooleanInclude submission date/time.
metadata.showIpAddressbooleanInclude submitter IP address.
metadata.showDevicebooleanInclude parsed device/browser info.
metadata.showReferrerbooleanInclude referrer URL.
metadata.showPageUrlbooleanInclude form host page URL.
metadata.showUtmParamsbooleanInclude UTM parameters (source, medium, campaign, etc).
settings.confirmationEmail
Auto-responder confirmation email sent to the form submitter.
enabledbooleanEnable confirmation email. Default: false.
emailFieldKeystringThe field key of the email field to send the confirmation to.
subjectstringEmail subject line. Supports merge tags. Default: "Thank you for contacting {site_name}".
bodyHtmlstringCustom HTML body (used when templateStyle is "custom"). Supports merge tags.
templateStylestring"html" (default thank-you template), "plain" (plain text), or "custom" (use bodyHtml).
replyTostringReply-to email address for the confirmation.
includeSummarybooleanInclude a summary of the submission data. Default: true.
attachmentsarrayStatic file attachments. Each: { name: string, url: string }.
settings.integrations
Webhook and third-party integrations.
webhooksarrayArray of webhook configurations. Each: { url, method, headers, events, enabled, payloadMode?, payloadTemplate? }.
Webhook Object Properties
enabled*booleanWhether this webhook is active.
url*stringHTTPS URL to send the webhook to.
method*string"POST" or "PUT".
headersobjectCustom 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".
payloadTemplatestringJSON 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
| Tag | Description |
|---|---|
| {{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 |
{{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.
customCssstringAdditional CSS scoped to this form.
customJsstringCustom JavaScript executed in the form context.
internalNamestringInternal name for organization (not shown to users).
descriptionstringInternal description/notes.
tagsstring[]Tags for organizing forms.
autoDeleteDaysnumberAuto-delete submissions after N days (0 = never). Coming soon — not yet active.
Top-level settings
submitButtonTextstringSubmit button label. Default: "Submit".
buttonIconstringIcon to show on the submit button (e.g. "arrow", "send", "check").
buttonIconPositionstring"left" or "right". Default: "right".
honeypotEnabledbooleanLegacy honeypot toggle (prefer security.honeypot).
multiStepbooleanMulti-step form mode. **Coming soon** — not yet supported in the embed script.
closedMessagestringMessage shown when form is archived/closed.
settings — Button Styling
Customize the submit button appearance beyond basic text and icon.
buttonHoverColorstringButton background color on hover (CSS color value).
buttonHoverTextColorstringButton text color on hover (CSS color value).
buttonBorderbooleanShow border on submit button. Default: false.
buttonBorderColorstringBorder color (CSS color value).
buttonShadowstringShadow preset: "none", "sm", "md", "lg". Default: "none".
settings — Disclaimer
Show disclaimer or consent text below the submit button.
disclaimerEnabledbooleanShow disclaimer text below submit button. Default: false.
disclaimerTextstringDisclaimer text content. Default: "By submitting this form, you agree to our Privacy Policy and Terms of Service."
disclaimerFontSizestringCSS font size (e.g. "12px", "0.75rem").
disclaimerColorstringText color (CSS color value).
disclaimerAlignstringText 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**syntaxRenders as bold text (<strong>).
*italic*syntaxRenders as italic text (<em>).
[text](url)syntaxRenders as a link. Opens in new tab with rel="noopener noreferrer".
~~strikethrough~~syntaxRenders 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.
emailOverridebooleanOverride site-level email settings for this form. Default: false.
emailFromNamestringCustom sender name for this form's notifications.
emailReplyTostringCustom reply-to address.
emailRecipientsstring[]Custom recipient list (overrides site recipients).
emailCcstringCC addresses.
emailBccstringBCC 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 Path | Flat Path | Notes |
|---|---|---|
| settings.thankYou.type | settings.successAction | "message" or "redirect" |
| settings.thankYou.redirectUrl | settings.successConfig.redirectUrl | URL for redirect after submission |
| settings.thankYou.message | settings.successConfig.body | Thank you message HTML |
| settings.security.honeypot | settings.honeypotEnabled | Bot honeypot toggle |
| settings.security.recaptchaEnabled | settings.recaptchaEnabled | reCAPTCHA v3 toggle |
| settings.security.allowedDomains | settings.allowedDomains | Domain restriction list |
| settings.security.rateLimitPerHour | settings.rateLimitPerIp | Rate limit per IP per hour |
| settings.advanced.customCss | customCss (top-level) | Top-level field is canonical |
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.
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).
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:
| Format | Stored Value | Merge Tags |
|---|---|---|
| "full" | Plain string: "John Doe" | {full_name} |
| "split" | Object: { "first": "John", "last": "Doe" } | {first_name}, {last_name}, {full_name} |
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
}
]
}'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.showStreet2booleanShow Street Address Line 2 (Apt, Suite, etc.). Default: true.
addressConfig.showCitybooleanShow City field. Default: true.
addressConfig.showStatebooleanShow State field. Default: true.
addressConfig.showZipbooleanShow Zip Code field. Default: true.
addressConfig.showCountrybooleanShow 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.stateDefaultstringPre-select a state (e.g. "UT", "AZ"). Works with both text and us modes.
addressConfig.stateLockedbooleanLock 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.
stateMode is set to "text", the state field also auto-capitalizes.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"
}
}
]
}'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*arrayArray of rule objects to evaluate.
rules[].fieldKey*stringThe 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[].valuestringValue to compare against. Optional for is_empty/is_not_empty operators.
actions*arrayArray of actions to execute when condition matches.
actions[].type*string"show", "hide", "require", "unrequire", or "set_value".
actions[].targetFieldKey*stringThe key of the field to act on.
actions[].valuestringValue 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*arrayArray of rule objects.
conditions.rules[].field*stringThe key of the field to check.
conditions.rules[].operator*stringComparison operator (same options as top-level).
conditions.rules[].valuestringValue to compare against.
conditions array automatically. The embed script only reads top-level conditions.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 -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"}
]
}
]
}'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.
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.fontSizestringGlobal input/option text font size (e.g. "15px"). Default: "15px".
theme.labelFontSizestringGlobal label/title font size (e.g. "15px"). Default: "15px".
fields[].fontSizenumberPer-field input font size override in px (10-20). Omit to inherit from theme.
fields[].labelFontSizenumberPer-field label font size override in px (10-20). Omit to inherit from theme.
Examples
Set redirect after submission
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 -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 -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 -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 -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 -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 -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
{
"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:
{
"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"
]
}
}version number increments with every update, which is useful for conflict detection.