Generate Form
Programmatically create a new form from a source template using the API
/api/v1/environments/{environmentId}/forms/generateGenerate a new form from a source template
Create a new form programmatically by providing a source form as a template and defining the steps and components for the new form. The generated form inherits all styling, fonts, backgrounds, and layout settings from the source form.
How It Works
- You provide a source form ID — this form acts as a style template. The generated form inherits its fonts, colors, backgrounds, and component styles.
- You define the steps and components for the new form. Each step contains one or more components (inputs, dropdowns, scheduling widgets, etc.).
- The API creates the form, publishes it, and returns the form ID and URL.
The source form must belong to the same environment and must not be deleted. Component types that are not in the source form's styles will automatically receive default styling.
Request
curl -X POST 'https://forms.withsurface.com/api/v1/environments/{environmentId}/forms/generate' \
-H 'x-api-key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"sourceFormId": "your-source-form-id",
"name": "My Generated Form",
"steps": [
{
"name": "Contact Info",
"components": [
{
"type": "IdentityInfo",
"content": {
"fields": [
{ "key": "firstName", "label": "First Name" },
{ "key": "lastName", "label": "Last Name" },
{ "key": "emailAddress", "label": "Email" }
]
}
},
{
"type": "NextButton",
"content": { "label": "Continue" }
}
]
},
{
"name": "Additional Details",
"components": [
{
"type": "Dropdown",
"content": {
"question": "What is your role?",
"options": ["Engineering", "Product", "Design", "Marketing", "Sales", "Other"]
}
},
{
"type": "NextButton",
"content": { "label": "Submit" }
}
]
}
]
}'Body Parameters
sourceFormIdbodystringrequiredThe ID of an existing form to use as a style template. The generated form inherits all visual settings (fonts, colors, backgrounds, component styles) from this form.
namebodystringrequiredDisplay name for the generated form.
stepsbodyarrayrequiredArray of step objects (minimum 2). Each step represents a page/screen in the form.
›Step object properties
steps[].namebodystringrequiredDisplay name for the step.
steps[].componentsbodyarrayrequiredArray of component objects (minimum 1). Each component is a form element within the step.
›Component object properties
typebodystringrequiredThe component type. See Supported Component Types below.
contentbodyobjectrequiredComponent-specific content. The shape depends on the component type.
Supported Component Types
Input Components
ShortInputbodycomponentSingle-line text input.
›Content fields
| Field | Type | Description |
|---|---|---|
question | string | The question/label text |
LongInputbodycomponentMulti-line text area.
›Content fields
| Field | Type | Description |
|---|---|---|
question | string | The question/label text |
IdentityInfobodycomponentCollects identity fields (name, email, company, etc.). This component has a strict schema — only the fields array is accepted.
›Content fields
| Field | Type | Required | Description |
|---|---|---|---|
fields | array | Yes | Array of field objects |
fields[].key | string | Yes | Field identifier. Valid keys: "firstName", "lastName", "fullName", "emailAddress", "workEmailAddress", "phoneNumber", "companyName", "numberOfEmployees", "websiteUrl", "title", "streetAddress", "aptSuiteEtc", "city", "state", "country", "zipCode", "companyRevenue", "profilePictureUrl" |
fields[].label | string | Yes | Display label |
fields[].placeholder | string | No | Placeholder text |
fields[].includeDialCode | boolean | No | Include dial code selector (for phone fields) |
fields[].defaultDialCodeCountry | string | No | Default country for dial code |
fields[].blockPersonalEmails | boolean | No | Block personal email domains (for email fields) |
fields[].revealAfter | string | No | Key of another field that must be filled before this one is shown |
Selection Components
DropdownbodycomponentDropdown select menu. Requires at least one option.
›Content fields
| Field | Type | Required | Description |
|---|---|---|---|
question | string | No | The question/label text |
options | string[] | Yes | Array of dropdown options (at least 1). You can also use choices as an alias. |
placeholder | string | No | Placeholder text |
isMulti | boolean | No | Allow multiple selections |
hideQuestion | boolean | No | Hide the question label |
MultipleOptionsQuestionbodycomponentSingle or multi-select options question.
›Content fields
| Field | Type | Description |
|---|---|---|
question | string | The question/label text |
choices | string[] | Array of choice options. You can also use options as an alias. |
subheading | string | Subheading text below the question |
isMulti | boolean | Allow multiple selections |
Scheduling Components
SchedulerbodycomponentEmbeds a calendar scheduling widget (supports Calendly, Cal.com, HubSpot, SavvyCal, RevenueHero, and more). Requires a calendar URL.
›Content fields
| Field | Type | Required | Description |
|---|---|---|---|
dataUrl | string | Yes | The calendar scheduling URL. You can also use url or calendarUrl as aliases. |
message | string | No | Message displayed above the calendar |
calendarType | string | No | Calendar provider: "Calendly", "Cal", "Hubspot", "SavvyCal", "RevenueHero", "ReclaimAI", "Clari", "ChiliCal" |
prefillEmail | boolean | No | Auto-fill email from earlier form steps |
Navigation Components
NextButtonbodycomponentButton to advance to the next step.
›Content fields
| Field | Type | Description |
|---|---|---|
label | string | Button text (alias for buttonText) |
Display Components
HeaderbodycomponentA text header displayed at the top of a step.
›Content fields
| Field | Type | Description |
|---|---|---|
title | string | The heading text |
subheading | string | Optional subheading text below the title |
DisclaimerbodycomponentA block of disclaimer or legal text.
›Content fields
| Field | Type | Description |
|---|---|---|
text | string | The disclaimer text content |
Other Components
Additional component types are supported: EmailForm, FileUploader, GraphicOptions, PasswordInput, and more. These follow the same pattern — pass the type and relevant content fields.
Response
successbooleanWhether the form was created successfully.
resultobject›Result object properties
formobject›Form object properties
idstringUnique identifier for the generated form.
urlstringThe public URL path for the form (e.g., /s/{formId}).
Example Response
{
"success": true,
"result": {
"form": {
"id": "cm5abc123def456",
"url": "/s/cm5abc123def456"
}
}
}Error Responses
{
"message": "Invalid request body",
"errors": [
{
"message": "CalendlyScreen requires a calendar URL (via 'dataUrl', 'url', or 'calendarUrl')",
"path": ["steps", 0, "components", 1, "content"]
}
]
}Form with Scheduling
Create a lead capture form with a scheduler step:
curl -X POST 'https://forms.withsurface.com/api/v1/environments/{environmentId}/forms/generate' \
-H 'x-api-key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"sourceFormId": "your-source-form-id",
"name": "Demo Booking Form",
"steps": [
{
"name": "Your Info",
"components": [
{
"type": "IdentityInfo",
"content": {
"fields": [
{ "key": "firstName", "label": "First Name" },
{ "key": "lastName", "label": "Last Name" },
{ "key": "emailAddress", "label": "Work Email" },
{ "key": "companyName", "label": "Company" }
]
}
},
{
"type": "NextButton",
"content": { "label": "Book a Demo" }
}
]
},
{
"name": "Schedule",
"components": [
{
"type": "CalendlyScreen",
"content": {
"dataUrl": "https://calendly.com/your-team/30min",
"message": "Pick a time that works for you"
}
}
]
}
]
}'