> ## Documentation Index
> Fetch the complete documentation index at: https://developer.meetergo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Routing Forms

> Build multi-step forms that collect data and route visitors to the right destination

Routing forms collect visitor data through single-page or multi-step funnels, then route the visitor to a meeting type, external URL, custom page, or contact form based on configurable rules.

## Key Concepts

| Concept          | Description                                                            |
| ---------------- | ---------------------------------------------------------------------- |
| **Data Field**   | Reusable form input (text, email, select, etc.) scoped to your company |
| **Routing Form** | Container for fields, funnel steps, and routing rules                  |
| **Funnel Step**  | One page/slide in a multi-step form                                    |
| **Qualifier**    | Routing rule with conditions and an action                             |
| **Recipient**    | Person who received a one-time form link                               |

## Structure Types

| Type               | Description                                     |
| ------------------ | ----------------------------------------------- |
| `FORM_ONLY`        | Single-page form                                |
| `FUNNEL_WITH_FORM` | Multi-step funnel followed by a final form page |
| `FUNNEL_ONLY`      | Multi-step funnel without a final form          |

## Create a Routing Form

Create a form with inline data fields, funnel steps, and routing rules in a single request:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.meetergo.com/v4/routing-form" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "x-meetergo-api-user-id: {userId}" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Lead Qualification",
      "structureType": "FUNNEL_WITH_FORM",
      "showProgressBar": true,
      "funnelSteps": [
        {
          "dataFields": [
            {
              "dataField": {
                "label": "Company Size",
                "fieldType": "select",
                "options": [
                  { "label": "1-10", "value": "small" },
                  { "label": "11-100", "value": "medium" },
                  { "label": "100+", "value": "enterprise" }
                ]
              }
            }
          ]
        }
      ],
      "fields": [
        { "dataFieldId": 1, "order": 0 },
        { "dataFieldId": 4, "order": 1 }
      ],
      "qualifiers": [
        {
          "routingAction": "eventRedirect",
          "meetingTypeId": "your-meeting-type-uuid",
          "expression": {
            "operator": "and",
            "operands": [{
              "operator": "equals",
              "target": "attendeeOther",
              "customTarget": "Company Size",
              "value": "enterprise"
            }]
          }
        },
        {
          "routingAction": "customPage",
          "isFallback": true,
          "customPageContent": {
            "en": "Thanks! We will be in touch.",
            "de": "Danke! Wir melden uns bei Ihnen."
          }
        }
      ]
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.meetergo.com/v4/routing-form', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'x-meetergo-api-user-id': userId,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      name: 'Lead Qualification',
      structureType: 'FUNNEL_WITH_FORM',
      showProgressBar: true,
      funnelSteps: [{
        dataFields: [{
          dataField: {
            label: 'Company Size',
            fieldType: 'select',
            options: [
              { label: '1-10', value: 'small' },
              { label: '11-100', value: 'medium' },
              { label: '100+', value: 'enterprise' }
            ]
          }
        }]
      }],
      fields: [
        { dataFieldId: 1, order: 0 },
        { dataFieldId: 4, order: 1 }
      ],
      qualifiers: [{
        routingAction: 'eventRedirect',
        meetingTypeId: 'your-meeting-type-uuid',
        expression: {
          operator: 'and',
          operands: [{
            operator: 'equals',
            target: 'attendeeOther',
            customTarget: 'Company Size',
            value: 'enterprise'
          }]
        }
      }, {
        routingAction: 'customPage',
        isFallback: true,
        customPageContent: { en: 'Thanks! We will be in touch.' }
      }]
    })
  });

  const routingForm = await response.json();
  ```
</CodeGroup>

## Cascade Creation

When creating or updating a routing form, data fields can be **referenced by ID** or **created inline**:

```javascript theme={null}
// Reference an existing data field
{ dataFieldId: 42, order: 0 }

// Create a new data field inline
{
  dataField: {
    label: 'Industry',
    fieldType: 'select',
    options: [
      { label: 'Tech', value: 'tech' },
      { label: 'Finance', value: 'finance' }
    ]
  },
  order: 1
}
```

This works in both `fields` (form fields) and `funnelSteps[].dataFields` (funnel step fields).

## Data Fields

Data fields are reusable inputs scoped to your company.

### Input Fields

| Type            | Description                                                                  |
| --------------- | ---------------------------------------------------------------------------- |
| `email`         | Email with validation                                                        |
| `phone`         | Phone number (optional country picker via `style` and `defaultCountryCode`)  |
| `text-single`   | Single-line text                                                             |
| `text-multi`    | Multi-line textarea                                                          |
| `number`        | Numeric input (supports `min`/`max`)                                         |
| `url`           | URL input with validation                                                    |
| `date`          | General date picker                                                          |
| `date-of-birth` | Date of birth picker (max date = today)                                      |
| `time`          | Time picker (HH:MM)                                                          |
| `yes-no`        | Binary yes/no button choice                                                  |
| `rating`        | Star rating (1–max stars, max defaults to 5)                                 |
| `currency`      | Numeric currency input (`placeholder` holds the currency symbol, default: €) |
| `location`      | Geolocation capture (stores `"lat,lng"` string)                              |

### Selection Fields

| Type              | Description                                                              |
| ----------------- | ------------------------------------------------------------------------ |
| `select`          | Dropdown (supports `displayStyle`: `default`, `card-icon`, `card-image`) |
| `radio`           | Radio buttons (supports `displayStyle`)                                  |
| `checkbox-single` | Single checkbox (e.g. consent)                                           |
| `checkbox-multi`  | Multiple checkboxes (supports `displayStyle`)                            |
| `slide`           | Funnel page with visual option cards (requires `slideOptions`)           |

### Composite Fields

| Type            | Description                                                                      | Options               |
| --------------- | -------------------------------------------------------------------------------- | --------------------- |
| `contact`       | Configurable contact data (name, email, phone, address)                          | `contactOptions`      |
| `bank-details`  | SEPA/bank details (IBAN always shown, BIC/account holder/signature configurable) | `bankDetailsOptions`  |
| `license-plate` | Vehicle license plate input (all countries or Germany-only)                      | `licensePlateOptions` |

### File & Document Fields

| Type            | Description                                                            | Options               |
| --------------- | ---------------------------------------------------------------------- | --------------------- |
| `file`          | File upload (Growth+)                                                  | `fileFieldOptions`    |
| `image`         | Image-only upload with optional camera capture                         | `fileFieldOptions`    |
| `signature`     | Canvas-based e-signature (stored as base64 PNG)                        | `requireSignatureOtp` |
| `pdf-template`  | PDF template with drag-and-drop field placement and optional signature | `pdfTemplateOptions`  |
| `file-download` | Display-only — shows library files the respondent can download         | `fileDownloadOptions` |

### Display-Only Fields

These fields do not collect input. They are used for layout and content within forms.

| Type         | Description                     | Notes                                                 |
| ------------ | ------------------------------- | ----------------------------------------------------- |
| `heading`    | Section heading                 | Uses `label` as text, `headingLevel` (1–6, default 2) |
| `separator`  | Visual separator line           | No configuration needed                               |
| `page-break` | Splits form into pages          | Optional title via `label`                            |
| `info-text`  | Rich text block (Markdown/HTML) | Uses `label` as content                               |
| `info-image` | Image block                     | Uses `label` as the image URL                         |
| `info-video` | Video embed (YouTube/Vimeo)     | Uses `label` as the video URL                         |

### Create Data Field

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.meetergo.com/v4/data-field" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "x-meetergo-api-user-id: {userId}" \
    -H "Content-Type: application/json" \
    -d '{
      "label": "Budget Range",
      "fieldType": "select",
      "required": true,
      "options": [
        { "label": "< $10k", "value": "small" },
        { "label": "$10k-$50k", "value": "medium" },
        { "label": "$50k+", "value": "large" }
      ],
      "locales": [
        { "locale": "de", "label": "Budgetbereich" }
      ]
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.meetergo.com/v4/data-field', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'x-meetergo-api-user-id': userId,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      label: 'Budget Range',
      fieldType: 'select',
      required: true,
      options: [
        { label: '< $10k', value: 'small' },
        { label: '$10k-$50k', value: 'medium' },
        { label: '$50k+', value: 'large' }
      ]
    })
  });
  ```
</CodeGroup>

## Routing Rules (Qualifiers)

Qualifiers determine where a visitor goes after submitting the form. They are evaluated in order; the first match wins.

### Routing Actions

| Action             | Description                      | Required Field                                                                 |
| ------------------ | -------------------------------- | ------------------------------------------------------------------------------ |
| `eventRedirect`    | Redirect to a meeting type       | `meetingTypeId` (optionally `meetingTypeUserId` to target a specific user)     |
| `customPage`       | Show localized thank-you page    | `customPageContent` (optionally `customFormTitle`, `showThankYouOnCustomPage`) |
| `externalRedirect` | Redirect to URL                  | `externalRedirectLink`                                                         |
| `contactForm`      | Show contact form                | `contactFormEmail`                                                             |
| `requestCallback`  | Request callback via queue       | `queueId`                                                                      |
| `instantCall`      | Start instant video call         | --                                                                             |
| `formRedirect`     | Redirect to another routing form | --                                                                             |

### Condition Expressions

Conditions use a nested AND/OR tree:

```json theme={null}
{
  "expression": {
    "operator": "and",
    "operands": [
      {
        "operator": "equals",
        "target": "attendeeOther",
        "customTarget": "Company Size",
        "value": "enterprise"
      },
      {
        "operator": "or",
        "operands": [
          { "operator": "contains", "target": "attendeeEmail", "value": "@bigcorp.com" },
          { "operator": "equals", "target": "attendeeOther", "customTarget": "Budget", "value": "large" }
        ]
      }
    ]
  }
}
```

**Operators:** `equals`, `notEqual`, `contains`, `notContains`, `startsWith`, `blank`, `notBlank`, `lessThan`, `greaterThan`, `lessThanOrEqual`, `moreThanOrEqual`, `containsAny`, `containsNone`

**Targets:** `attendeeEmail`, `attendeeFullname`, `attendeeFirstname`, `attendeeLastname`, `attendeePhone`, `attendeePolicyAccepted`, `attendeeReceiveReminders`, `attendeeLanguage`, `attendeeTimezone`, `attendeeOther` (use `customTarget` for custom field name)

### Fallback Qualifier

If no qualifier has `isFallback: true`, a default "Thanks for submitting" custom page is auto-created.

## Updating with Declarative Sync

When updating via `PATCH`, nested arrays use different strategies:

* **Qualifiers**: Declarative sync -- items with `id` are updated, without `id` are created, missing items are removed
* **Funnel Steps & Fields**: Full replace -- the array you send replaces the current set

```javascript theme={null}
// Update: keep one qualifier, add another, remove the rest
await fetch(`https://api.meetergo.com/v4/routing-form/${formId}`, {
  method: 'PATCH',
  headers,
  body: JSON.stringify({
    qualifiers: [
      { id: 'existing-qualifier-uuid', routingAction: 'customPage' },
      { routingAction: 'eventRedirect', meetingTypeId: 'new-mt-uuid',
        expression: { operator: 'and', operands: [/*...*/] } }
    ]
  })
});
```

## Sending Forms

Send a routing form to a recipient via email, SMS, or generate a one-time link.

### Delivery Methods

| Method            | Description                    | Required |
| ----------------- | ------------------------------ | -------- |
| `email` (default) | Sends email with one-time link | `email`  |
| `sms`             | Sends SMS with one-time link   | `phone`  |
| `link`            | Generates one-time link only   | --       |

### Send via Email

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.meetergo.com/v4/routing-form/{formId}/send" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "x-meetergo-api-user-id: {userId}" \
    -H "Content-Type: application/json" \
    -d '{
      "recipientName": "John Smith",
      "email": "john@example.com",
      "message": "Please fill out this form before our meeting.",
      "deliveryMethod": "email"
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    `https://api.meetergo.com/v4/routing-form/${formId}/send`,
    {
      method: 'POST',
      headers,
      body: JSON.stringify({
        recipientName: 'John Smith',
        email: 'john@example.com',
        message: 'Please fill out this form before our meeting.',
        deliveryMethod: 'email'
      })
    }
  );

  const { publicUrl, status } = await response.json();
  // publicUrl: "https://cal.meetergo.com/f/r/abc123..."
  // status: "sent"
  ```
</CodeGroup>

### Generate One-Time Link (No Notification)

```javascript theme={null}
const { publicUrl } = await fetch(
  `https://api.meetergo.com/v4/routing-form/${formId}/send`,
  {
    method: 'POST',
    headers,
    body: JSON.stringify({
      recipientName: 'Partner',
      deliveryMethod: 'link'
    })
  }
).then(r => r.json());

// Distribute publicUrl however you like
```

The response always includes `publicUrl` regardless of delivery method.

### Shareable Public Link (Unlimited)

For unlimited-access forms, set a `slug`:

```javascript theme={null}
await fetch(`https://api.meetergo.com/v4/routing-form/${formId}`, {
  method: 'PATCH',
  headers,
  body: JSON.stringify({ slug: 'my-lead-form' })
});
// Public URL: https://cal.meetergo.com/f/my-lead-form
```

## One-Time Fill

When sent to a recipient, each link can only be submitted once:

1. **Send** -- `POST /v4/routing-form/:id/send` creates a recipient with a unique token
2. **Open** -- Recipient opens the link, `openedAt` is recorded
3. **Submit** -- Recipient submits, `completedAt` is recorded
4. **Blocked** -- Re-submission returns `409 Conflict`

### Track Recipients

```javascript theme={null}
const { recipients } = await fetch(
  `https://api.meetergo.com/v4/routing-form/${formId}/recipients`,
  { headers }
).then(r => r.json());

// Each recipient has: status ("sent" | "opened" | "completed"),
// publicUrl, sentAt, openedAt, completedAt
```

## API Reference

| Method   | Endpoint                          | Description         |
| -------- | --------------------------------- | ------------------- |
| `POST`   | `/v4/routing-form`                | Create routing form |
| `GET`    | `/v4/routing-form`                | List routing forms  |
| `GET`    | `/v4/routing-form/:id`            | Get routing form    |
| `PATCH`  | `/v4/routing-form/:id`            | Update routing form |
| `DELETE` | `/v4/routing-form/:id`            | Delete routing form |
| `POST`   | `/v4/routing-form/:id/send`       | Send to recipient   |
| `GET`    | `/v4/routing-form/:id/recipients` | List recipients     |
| `POST`   | `/v4/data-field`                  | Create data field   |
| `GET`    | `/v4/data-field`                  | List data fields    |
| `GET`    | `/v4/data-field/:id`              | Get data field      |
| `PATCH`  | `/v4/data-field/:id`              | Update data field   |
| `DELETE` | `/v4/data-field/:id`              | Delete data field   |

## Next Steps

<CardGroup cols={2}>
  <Card title="Routing & Round-Robin" icon="arrows-split-up-and-left" href="/developer-docs/core-concepts/routing">
    Distribute bookings across team members
  </Card>

  <Card title="Meeting Types" icon="calendar" href="/developer-docs/core-concepts/meeting-types">
    Configure meeting templates
  </Card>
</CardGroup>
