Page Step

Show a confirmation, thank-you, or informational page with headings, rich text, third-party embeds, and links. Use it as a landing page or as a pause-and-continue screen between other steps.

The Page step displays a content page during a workflow. Use it to thank a participant, confirm a submission, embed a scheduling widget, or link out to an external site. Unlike a Forms step, a Page does not collect input and does not produce fields that later steps can map from.

A Page can be the first step in a workflow and it can be the last. When it is not the last step, set waitForContinue to control whether the workflow waits for the participant to click Continue or advances on its own. See Controlling when the workflow advances.

In the project payload, set the step's type to page. The JSON below is the step configuration that belongs in workflow.steps[].config.

Top-level configuration

nametyperequireddescription
typestringnoDiscriminator for the step configuration. Set to page when present.
componentsarrayyesOrdered list of components. Put exactly one page container here; nest every other component inside it.
waitForContinuebooleannoWhether the session waits for the participant to click Continue. Default: false.

Component types

A page is built from an ordered list of components. Each component has a type field that determines its behavior and available properties.

Common component properties

Every component supports these base properties in addition to its type-specific ones:

nametyperequireddescription
idstringnoStable component id. Auto-generated if omitted.
typestringyesThe component type.
labelstringnoInternal label for the component.
parentIdstringnoID of the parent component, if nested.

Page container

The page container is the canvas that holds every other component. Place one page component in the top-level components array and nest headings, descriptions, embeds, and buttons inside it.

nametyperequireddescription
typestringyesMust be page.
componentsarraynoNested components: heading, description, code_block, external_button_group, external_button, or continue_button.

See Page content for the single-page expectation.

Heading

Use for the page title or section titles.

nametyperequireddescription
typestringyesMust be heading.
presentation.contentstringnoHeading text.
presentation.levelnumbernoHeading level: 1, 2, or 3. Default: 1.
presentation.textAlignstringnoAlignment: left, center, or right. Default: left.

Description

Use for body copy. Description content is HTML and is the only page component that supports mapping placeholders. Placeholders such as {{Form.first_name}} are replaced with values from earlier steps when the page is shown.

nametyperequireddescription
typestringyesMust be description.
presentation.contentstringnoHTML body content. Mapping placeholders are resolved at display time.
presentation.textAlignstringnoAlignment: left, center, or right. Default: left.

A few things to note:

  • Allowed HTML includes headings, paragraphs, lists, tables, links (a), images (img), figures, and inline formatting (strong, em, u, and similar).
  • <script>, <iframe>, <form>, <style>, and event-handler attributes (for example onclick) are removed.
  • a links accept http, https, and mailto URLs. img sources accept http and https URLs.

Code block

Use to embed third-party HTML such as a scheduling widget, chat snippet, or analytics tag. The snippet is rendered in an isolated frame, separate from the rest of the session.

nametyperequireddescription
typestringyesMust be code_block.
presentation.languagestringnoSnippet language. Use html for embed snippets.
presentation.contentstringnoThe HTML or script to embed.
📘

Code-block content is not sanitized. Only include snippets from sources you trust.

External button group

A container for outbound link buttons. Nest external_button components inside it. A group shows up to three buttons; additional buttons are ignored.

nametyperequireddescription
typestringyesMust be external_button_group.
componentsarraynoNested external_button components (up to three).

External button

A button that opens an external URL. Nest it inside an external_button_group.

nametyperequireddescription
typestringyesMust be external_button.
presentation.labelstringnoButton label. Default: "Button".
presentation.urlstringnoAbsolute http or https URL. Other schemes do not open when clicked.
presentation.variantstringnoVisual style: primary, secondary, or tertiary. Default: primary.
presentation.openInNewTabbooleannoWhether the URL opens in a new tab. Default: true.

Continue button

Advances the workflow to the next step. Include this only when waitForContinue is true; otherwise it is removed. See Controlling when the workflow advances.

nametyperequireddescription
typestringyesMust be continue_button.
presentation.labelstringnoButton label. Default: "Next step".
presentation.variantstringnoVisual style: primary, secondary, or tertiary. Default: primary.

Page content

Put exactly one page container in the top-level components array. Every heading, description, embed, and button belongs inside that container — not as a sibling of it.

A few things to note:

  • If you send more than one page container, only the first is used.
  • A page container cannot be nested inside another page container.
  • An external_button_group shows at most three external_button children. Extra buttons are ignored.
  • Unrecognized type values are ignored.
📘

Sending extra page containers, nested pages, extra buttons, or unknown component types does not fail the request. The extra content is simply not shown.

Controlling when the workflow advances

waitForContinue decides whether the participant must click Continue before the workflow moves on.

  • true — the session waits for the participant to click Continue. If you omit continue_button, one is added for you. If you include more than one, the first is used.
  • false (default) — the page is shown and the workflow advances on its own. Any continue_button you send is removed.

When a Page is the last step and waitForContinue is true, clicking Continue completes the session.

Examples

A minimal page with a heading. waitForContinue is omitted, so it defaults to false and the workflow advances without a Continue button:

{
  "type": "page",
  "components": [
    {
      "id": "835b17f8-f133-434e-a8d1-b43e1249557d",
      "type": "page",
      "label": "Page",
      "components": [
        {
          "id": "page-heading",
          "type": "heading",
          "label": "Header",
          "parentId": "835b17f8-f133-434e-a8d1-b43e1249557d",
          "presentation": {
            "content": "Thank you",
            "level": 1,
            "textAlign": "left"
          }
        }
      ]
    }
  ]
}

Building on that, here's a confirmation page that waits for Continue. It greets the participant with a mapped name from an earlier Forms step, embeds a scheduling widget, and offers two outbound links:

{
  "type": "page",
  "waitForContinue": true,
  "components": [
    {
      "id": "835b17f8-f133-434e-a8d1-b43e1249557d",
      "type": "page",
      "label": "Page",
      "components": [
        {
          "id": "page-heading",
          "type": "heading",
          "label": "Header",
          "parentId": "835b17f8-f133-434e-a8d1-b43e1249557d",
          "presentation": {
            "content": "You're all set",
            "level": 1,
            "textAlign": "left"
          }
        },
        {
          "id": "page-description",
          "type": "description",
          "label": "Description",
          "parentId": "835b17f8-f133-434e-a8d1-b43e1249557d",
          "presentation": {
            "content": "<p>Thanks, {{`Form`.`first_name`}}. We've received your information.</p>",
            "textAlign": "left"
          }
        },
        {
          "id": "page-embed",
          "type": "code_block",
          "label": "Code block",
          "parentId": "835b17f8-f133-434e-a8d1-b43e1249557d",
          "presentation": {
            "language": "html",
            "content": "<div class=\"calendly-inline-widget\" data-url=\"https://calendly.com/example/intro\"></div>"
          }
        },
        {
          "id": "page-buttons",
          "type": "external_button_group",
          "label": "External buttons",
          "parentId": "835b17f8-f133-434e-a8d1-b43e1249557d",
          "components": [
            {
              "id": "btn-site",
              "type": "external_button",
              "label": "Primary",
              "parentId": "page-buttons",
              "presentation": {
                "label": "Visit our site",
                "url": "https://example.com",
                "variant": "primary",
                "openInNewTab": true
              }
            },
            {
              "id": "btn-help",
              "type": "external_button",
              "label": "Secondary",
              "parentId": "page-buttons",
              "presentation": {
                "label": "Get help",
                "url": "https://example.com/help",
                "variant": "secondary",
                "openInNewTab": true
              }
            }
          ]
        },
        {
          "id": "page-continue",
          "type": "continue_button",
          "label": "Continue",
          "parentId": "835b17f8-f133-434e-a8d1-b43e1249557d",
          "presentation": {
            "label": "Continue",
            "variant": "primary"
          }
        }
      ]
    }
  ]
}

A few things to note:

  • Every nested component sets parentId to its parent: children of the page container use the page id, and buttons inside the group use the group's id.
  • The description uses a mapping placeholder ({{Form.first_name}}) so the greeting is filled in from an earlier Forms step named Form. See Mapping.
  • waitForContinue is true, so the Continue button is shown and the session waits for a click. If this page is the last step, that click completes the session.

Did this page help you?