---
title: "By Example: How Truto Helps Engineers Build Faster Integrations - Notion"
slug: introducing-our-notion-integration
date: 2024-04-18
author: Uday Gajavalli
categories: [By Example]
excerpt: "Build post-connection configuration UI for SaaS integrations with Truto's RapidForm. Declarative JSON config, cascading selects, pagination for 40K+ pages, and JSONata validation."
tldr: "Truto's RapidForm lets you build post-connection configuration UIs declaratively - cascading selects, pagination for 40K+ items, JSONata validation, and automatic persistence to account context. No custom UI code per connector."
canonical: https://truto.one/blog/introducing-our-notion-integration/
---

# By Example: How Truto Helps Engineers Build Faster Integrations - Notion


Notion is a powerhouse. To leverage it to the fullest, you also need a powerful integration.

With our Notion integration rollout, we believe we've exceeded expectations - the integration supports reading from over 40,000 pages. It also offers an upgrade over the standard Notion page selector by introducing the ability to select pages based on parent-child relationships, among other features.

The integration enables users to connect their Notion accounts and select the files they wish to sync. This feature is incorporated with [RapidForm](https://truto.one/docs/guides/rapid-form/overview). RapidForm is our solution that allows your users to easily choose specific files or folders in their accounts.

This approach also helps ensure that you maintain strong data integrity and privacy by requesting access only to specific files and pages from your customers.

Enabling RapidForm will help your engineering team skip over tedious UI tasks, and save time.

## How  it works

-   Connect a Notion account
    

! [OAuth 2.0 connection screen for Notion featuring the Notion logo and a blue Connect button.](https://truto.one/images/content/introducing-our-notion-integration-0.png)

-   Use the OAuth app provided by Truto or your OAuth app to authorize
    

! [Notion authorization page where Truto requests permissions to view and edit selected workspace pages.](https://truto.one/images/content/introducing-our-notion-integration-1.png)

-   Chooses specific files to sync
    

The Notion form you see below is completely customizable. 

! [Truto's RapidForm UI showing a searchable checklist to select specific Notion pages for connection.](https://truto.one/images/content/introducing-our-notion-integration-2.png)

This RapidForm UI represents a significant improvement over the standard Notion page selector. Truto's RapidForm enables customization of the form field, including parent-child selectors, and provides a separate tab for viewing all selected pages. During our tests, it easily accommodated large Notion accounts with over 40,000 pages.

-   RapidForm can be initialized at any point using our [Link SDK](https://www.npmjs.com/package/@truto/truto-link-sdk#open-the-rapidform-ui). This will enable your users to select additional files or pages at a later point in time.
    
-   All pages are stored as variables for easy access in RapidBridge and other places. You can learn more about variables [here](https://truto.one/docs/guides/integrated-accounts/adding-editing-context-variables).
    

! [JSON output showing selected Notion pages stored as variables with unique IDs and labels like ICP and Marketing.](https://truto.one/images/content/introducing-our-notion-integration-3.png)

## Building a Post-Connection Configuration UI with RapidForm

The OAuth handshake is just the beginning of any SaaS integration. What comes next is the harder problem: letting your users scope exactly which data they want to sync. Most teams end up building custom post-connection configuration UIs for every connector - a workspace picker for Asana, a channel selector for Slack, a page browser for Notion. Each one requires its own API calls, pagination logic, dependent dropdowns, and state management.

RapidForm is Truto's declarative answer to this. Instead of writing bespoke connector UI post-connection flows for each integration, you define a JSON configuration that describes what to collect from users. Truto handles the rendering, API calls, pagination, and persistence. One pattern works across every integration.

### When to use RapidForm

- **Scoping sync to specific resources**: Let users pick which Notion pages, Asana projects, Zendesk tags, or Slack channels to sync - instead of pulling everything.
- **Collecting integration-specific settings**: Gather API keys, subdomain info, or configuration values that the OAuth flow doesn't capture.
- **Enforcing data minimization**: Only pull data your users explicitly approve. This matters for compliance and customer trust.
- **Dynamic, dependent selections**: Show projects within a workspace, or child pages within a parent - where one selection drives the next.

### RapidForm JSON Configuration and Field Types

A RapidForm is defined as a JSON object with a `type` of `"form"` and an array of field definitions. Here's the structure for a Notion-style page selector:

```json
{
  "type": "form",
  "config": {
    "fields": [
      {
        "name": "pages",
        "type": "multi_select",
        "label": "Notion Pages",
        "help_text": "Select the pages you want to sync",
        "required": true,
        "data_source": {
          "type": "unified",
          "resource": "documents/pages",
          "method": "list"
        },
        "options": {
          "value": "id",
          "label": "name",
          "parent": "parent_id"
        }
      }
    ]
  }
}
```

The `parent` attribute in `options` enables the parent-child grouping visible in the Notion screenshot above - Truto renders the page hierarchy so users can browse pages the way they do in Notion itself.

RapidForm supports several field types, each suited to different post-connection configuration needs:

| Type | Use Case | Example |
|------|----------|--------|
| `single_select` | Choose one option from a dropdown | Select a workspace |
| `multi_select` | Choose multiple options from a list | Select pages or projects to sync |
| `text` | Free-form text input | Enter a custom domain or filter |
| `password` | Sensitive input (masked) | Enter an API secret |
| `checkbox` | Boolean toggle | Enable or disable a sync option |
| `hidden` | Store computed or derived values | Session tokens, metadata |

Each field has a `data_source` that tells RapidForm which API to call (Unified or Proxy) and which resource and method to use. The `options` object maps response attributes to the label, value, and optional subtext shown in the UI.

### Cascading Selects and Pagination

Real-world integrations rarely have flat option lists. Users have workspaces containing projects containing tasks, or parent pages containing child pages. RapidForm handles this with two mechanisms: field dependencies and built-in pagination.

**Cascading dependencies** let one field's options depend on another field's value. Here's a config where a workspace selector drives a project selector:

```json
{
  "type": "form",
  "config": {
    "fields": [
      {
        "name": "workspace_id",
        "type": "single_select",
        "label": "Workspace",
        "help_text": "Select the workspace you want to sync",
        "placeholder": "Select a workspace",
        "required": true,
        "data_source": {
          "type": "unified",
          "resource": "ticketing/workspaces",
          "method": "list"
        },
        "options": {
          "value": "id",
          "label": "name",
          "subText": "id"
        }
      },
      {
        "name": "collections",
        "type": "multi_select",
        "label": "Projects",
        "depends_on": ["workspace_id"],
        "help_text": "The projects to sync",
        "required": true,
        "disabled_text": "Please select a workspace",
        "data_source": {
          "type": "unified",
          "resource": "ticketing/collections",
          "method": "list",
          "query": {
            "workspace_id": "{{workspace_id}}"
          }
        },
        "options": {
          "value": "id",
          "label": "name",
          "subText": "id"
        }
      }
    ]
  }
}
```

How it works:

- **`depends_on`** accepts an array of field names. The dependent field stays disabled (showing `disabled_text`) until its parent has a value.
- The **`query`** object in `data_source` uses `{{workspace_id}}` placeholder syntax to inject the parent field's selected value into the API call.
- When a parent field changes, dependent fields **reset automatically** so stale selections don't persist.
- The order of fields in the `config.fields` array determines the order they appear in the UI.

**Pagination** is built in. For accounts with thousands of items - like our Notion integration handling 40,000+ pages - RapidForm paginates through results automatically. Users see a "Load more" button to fetch additional pages of data. For smaller datasets, you can enable auto-pagination to load everything as the user scrolls, but be careful with large datasets.

For multi-select fields where users might select 1,000+ items, set the `high_cardinality` flag to optimize how selections are stored and transmitted.

### JSONata Validation Examples

Basic required/optional validation is built into each field. For more complex rules - like "select at least 5 projects" or cross-field validation - RapidForm supports custom validation using JSONata expressions.

Here's a validation that enforces a minimum selection count:

```jsonata
($exists(pages) and $count(pages) >= 1)
  ? undefined
  : [{ "field": "pages", "message": "Select at least one page to sync" }]
```

The expression receives the current form state as input and returns either `undefined` (validation passes) or an array of error objects targeting specific fields. This runs before the form can be submitted, giving users immediate feedback.

You can also use JSONata **transform expressions** to compute derived fields before saving. For example, adding a count of selected items and a timestamp:

```jsonata
$merge([
  $,
  {
    "total_selected": $count(pages),
    "configured_at": $now()
  }
])
```

The `$merge` function combines the original form data (`$`) with computed fields. The result is what gets persisted to the account context.

### How RapidForm Persists Configuration to Account Context

Once a user submits the form, RapidForm saves all selected values as **variables** (also called context) on the integrated account. These variables are stored as a JSON object and are available everywhere in Truto - sync jobs, Proxy API calls, and custom automations.

For example, after a user selects three Notion pages in RapidForm, the integrated account's context includes:

```json
{
  "pages": [
    { "value": "abc-123", "label": "Product Roadmap" },
    { "value": "def-456", "label": "Engineering Wiki" },
    { "value": "ghi-789", "label": "Marketing Plans" }
  ]
}
```

In a [RapidBridge sync job](https://truto.one/docs/guides/rapid-bridge/creating-rapid-bridge), you reference these variables using placeholder syntax:

```json
{
  "resources": [
    {
      "resource": "documents/pages",
      "method": "get",
      "loop_on": "{{pages}}",
      "id": "{{pages}}"
    }
  ]
}
```

The `loop_on` directive iterates over the selected pages, fetching each one individually. This is exactly how the 40,000-page Notion integration works - users pick what they need through RapidForm, and the sync job only pulls those pages.

The flow looks like this:

```mermaid
sequenceDiagram
    participant User
    participant RapidForm
    participant Truto
    participant Notion API

    User->>RapidForm: Opens post-connection UI
    RapidForm->>Truto: Fetch pages (Unified API)
    Truto->>Notion API: GET /pages
    Notion API-->>Truto: Page list (paginated)
    Truto-->>RapidForm: Render page tree
    User->>RapidForm: Selects pages + submits
    RapidForm->>Truto: Save selections to<br>account context
    Truto-->>User: Sync job uses<br>selected pages only
```

### Embedding RapidForm in Your App

RapidForm renders automatically as part of Truto's account connection flow. But you can also trigger it independently at any point using the [Link SDK](https://www.npmjs.com/package/@truto/truto-link-sdk#open-the-rapidform-ui) - useful for letting users update their sync preferences after initial setup.

This means your post-connection configuration UI isn't a one-time gate. Users can come back and add or remove pages, change workspace selections, or update any configuration - without reconnecting their account. For your engineering team, this eliminates the need to build and maintain custom settings UIs for each integration.

## Try out the Notion integration

Want to try out the integration? Get in touch with us at support@truto.one or [book a demo](https://truto.one/?ref=blog-notion-integration).

## Truto SuperQuery

This also marks the birth of **Truto SuperQuery** \- our solution to help you filter data even when the underlying API lacks the necessary filters.

With Truto SuperQuery and Truto Real-time, businesses can fulfill all their desired use cases without any compromises. [Discover more about the trade-offs between real-time and cached unified APIs here](https://truto.one/blog/tradeoffs/) to determine which version of Truto aligns best with your specific needs.
