API ReferenceForms

Generate Form

Programmatically create a new form from a source template using the API

POST/api/v1/environments/{environmentId}/forms/generate

Generate 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.

You'll need an API key to use this endpoint. See the API Keys documentation to create one.

How It Works

  1. You provide a source form ID — this form acts as a style template. The generated form inherits its fonts, colors, backgrounds, and component styles.
  2. You define the steps and components for the new form. Each step contains one or more components (inputs, dropdowns, scheduling widgets, etc.).
  3. 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

sourceFormIdbodystringrequired

The 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.

namebodystringrequired

Display name for the generated form.

stepsbodyarrayrequired

Array of step objects (minimum 2). Each step represents a page/screen in the form.

Step object properties
steps[].namebodystringrequired

Display name for the step.

steps[].componentsbodyarrayrequired

Array of component objects (minimum 1). Each component is a form element within the step.

Component object properties
typebodystringrequired

The component type. See Supported Component Types below.

contentbodyobjectrequired

Component-specific content. The shape depends on the component type.

Supported Component Types

Input Components

ShortInputbodycomponent

Single-line text input.

Content fields
FieldTypeDescription
questionstringThe question/label text
LongInputbodycomponent

Multi-line text area.

Content fields
FieldTypeDescription
questionstringThe question/label text
IdentityInfobodycomponent

Collects identity fields (name, email, company, etc.). This component has a strict schema — only the fields array is accepted.

Content fields
FieldTypeRequiredDescription
fieldsarrayYesArray of field objects
fields[].keystringYesField identifier. Valid keys: "firstName", "lastName", "fullName", "emailAddress", "workEmailAddress", "phoneNumber", "companyName", "numberOfEmployees", "websiteUrl", "title", "streetAddress", "aptSuiteEtc", "city", "state", "country", "zipCode", "companyRevenue", "profilePictureUrl"
fields[].labelstringYesDisplay label
fields[].placeholderstringNoPlaceholder text
fields[].includeDialCodebooleanNoInclude dial code selector (for phone fields)
fields[].defaultDialCodeCountrystringNoDefault country for dial code
fields[].blockPersonalEmailsbooleanNoBlock personal email domains (for email fields)
fields[].revealAfterstringNoKey of another field that must be filled before this one is shown

Selection Components

Dropdownbodycomponent

Dropdown select menu. Requires at least one option.

Content fields
FieldTypeRequiredDescription
questionstringNoThe question/label text
optionsstring[]YesArray of dropdown options (at least 1). You can also use choices as an alias.
placeholderstringNoPlaceholder text
isMultibooleanNoAllow multiple selections
hideQuestionbooleanNoHide the question label
MultipleOptionsQuestionbodycomponent

Single or multi-select options question.

Content fields
FieldTypeDescription
questionstringThe question/label text
choicesstring[]Array of choice options. You can also use options as an alias.
subheadingstringSubheading text below the question
isMultibooleanAllow multiple selections

Scheduling Components

Schedulerbodycomponent

Embeds a calendar scheduling widget (supports Calendly, Cal.com, HubSpot, SavvyCal, RevenueHero, and more). Requires a calendar URL.

Content fields
FieldTypeRequiredDescription
dataUrlstringYesThe calendar scheduling URL. You can also use url or calendarUrl as aliases.
messagestringNoMessage displayed above the calendar
calendarTypestringNoCalendar provider: "Calendly", "Cal", "Hubspot", "SavvyCal", "RevenueHero", "ReclaimAI", "Clari", "ChiliCal"
prefillEmailbooleanNoAuto-fill email from earlier form steps
NextButtonbodycomponent

Button to advance to the next step.

Content fields
FieldTypeDescription
labelstringButton text (alias for buttonText)

Display Components

Headerbodycomponent

A text header displayed at the top of a step.

Content fields
FieldTypeDescription
titlestringThe heading text
subheadingstringOptional subheading text below the title
Disclaimerbodycomponent

A block of disclaimer or legal text.

Content fields
FieldTypeDescription
textstringThe 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

successboolean

Whether the form was created successfully.

resultobject
Result object properties
formobject
Form object properties
idstring

Unique identifier for the generated form.

urlstring

The public URL path for the form (e.g., /s/{formId}).

Example Response

200 - Success
{
  "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"
            }
          }
        ]
      }
    ]
  }'

On this page