Skip to main content
The Headless Connect Portal can be used with your own UI components or design system to make your integrations feel native to the design of your app, while leveraging the SDK for all of the backend details of connecting and configuring your users’ integrations. Headless mode still provides fully managed authentication, so you don’t need to worry about managing, storing, or refreshing your customers’ credentials.

Example Implementation

Below is an end-to-end example implemented with React and shadcn/ui components (no pre-built Paragon components are used):

An example implementation of the Headless Connect Portal, using shadcn/ui components and the Paragon SDK

We have open-sourced the above implementation in a public repository here:

useparagon/connect-headless-example

An end-to-end implementation example of the Headless Connect Portal using React
You can use the example implementation or any of its components as a starting point for your application, or read on to learn how to implement it yourself.

Usage

To implement the Headless Connect Portal, use the SDK to implement the following product surfaces in your app:
  • Integrations Catalog: Listing all available integrations in a catalog to allow your users to discover integrations and connect their accounts.
  • Integration Install / Uninstall Flow: Guiding your user through the install stages of any integration, including the OAuth 2.0 flow (if required for the integration)
  • User Settings and Workflow Configuration: Providing inputs for users to configure User Settings for their integration.
Follow the sections below to learn how to implement each of these product surfaces using the Paragon SDK.
Have you implemented SDK authentication?The Usage guide below assumes that you have already implemented the basics of SDK authentication described in Getting Started. All of the functions below should be called after SDK authentication is completed.

Enabling Headless Mode

To turn on Headless mode for the SDK, call the setHeadless() function:
import { paragon } from "@useparagon/connect";

paragon.setHeadless(true);
This only needs to be called once after SDK initialization to use the Headless Connect Portal functions.

Integrations Catalog

In your app’s Integrations Catalog or Settings view, list available integrations from your Paragon project to allow your users to discover integrations and connect their accounts:
You can render this view by calling paragon.getIntegrationMetadata():
paragon.getIntegrationMetadata();
Each entry in the returned list will be an object with the integration’s display metadata, including the brand name, icon, and accent color:
[
  {
    type: "salesforce",
    name: "Salesforce",
    brandColor: "#057ACF",
    icon: "https://cdn.useparagon.com/latest/dashboard/public/integrations/salesforce.svg",
  },
];

Integration Detail View

As a part of your catalog, you may want to show users additional information when they click on an integration (or inline with the catalog):
Headless Connect Portal modal

A detail view of the Slack integration, after clicking on the Slack entry in the Integrations Catalog

To get the additional user-facing descriptions and informational text about the integration configured in your Paragon dashboard, use .getIntegrationConfig:
paragon.getIntegrationConfig("slack");
This will return an object with the descriptions, User Settings, and Workflows associated with the integration:
Response
{
  "shortDescription": "Send notifications to Slack",
  "longDescription": "Connect your Slack workspace to receive notifications and alerts in Slack. Stay connected to important activity by bringing it all together in your Slack workspace.\n\nOur Slack integration enables you to:\n\n• Receive alerts and notifications in your Slack workspace\n• Notify or DM specific team members based on certain activity",
  "availableUserSettings": [
    {
      "id": "2d5662c9-6750-46c2-8588-2ac904532efb",
      "type": "DYNAMIC_ENUM",
      "title": "Channel",
      "required": false,
      "sourceType": "channels"
    }
  ],
  "availableWorkflows": [
    {
      "id": "2248335c-671c-47e4-b9a0-3641a9f2d301",
      "inputs": [],
      "infoText": "Send a Slack notification when a Task is created",
      "defaultEnabled": false,
      "description": "Send Slack Notification"
    }
  ],
  "hiddenWorkflows": []
}

Displaying Account State

To display your user’s account connection status in the Integrations Catalog (for example, showing a “Manage” button instead of a “Connect” button for integrations they have already installed), call .getUser to get the user’s account state and .subscribe to react to state changes.
Getting account state with getUser
paragon.getUser();

// Returns:
{
  "authenticated": true,
  "userId": "user-id",
  "integrations": {
    "salesforce": {
      "enabled": false,
      "configuredWorkflows": {}
    },
    "slack": {
      "enabled": true,
      "configuredWorkflows": {},
      "credentialStatus": "VALID",
      "credentialId": "81af6717-9476-458d-8c29-f0aee7ce6d12",
      "providerId": "TM7FL705V",
      "providerData": {}
    },
    "hubspot": {
      "enabled": false,
      "configuredWorkflows": {}
    }
  },
  "meta": {}
}
We can use the result of this object to conditionally show a Connect or Manage button, depending on the value of user.integrations[integrationType].enabled. You can also react to changes in your UI by setting up .subscribe handlers:
Example component using subscribe() to react to user state changes
function MyComponent() {
  const [user, setUser] = useState<AuthenticatedConnectUser | undefined>();

  // Listen for account state changes
  useEffect(() => {
    const listener = () => {
      if (paragon) {
        const authedUser = paragon.getUser();
        if (authedUser.authenticated) {
          setUser({ ...authedUser });
        }
      }
    };
    listener();
    paragon?.subscribe("onIntegrationInstall", listener);
    paragon?.subscribe("onIntegrationUninstall", listener);
    return () => {
      paragon?.unsubscribe("onIntegrationInstall", listener);
      paragon?.unsubscribe("onIntegrationUninstall", listener);
    };
  }, [paragon]);
}

SDK Reference

Learn more about getUser() in the SDK Reference.

Integration Install / Uninstall Flow

Once a user expresses intent to connect their account, guide them through an Install Flow using the Paragon SDK:

Install Flow example for Shopify

To implement an Install Flow, your app will need to handle the following stages of the installation process which should take place in your app (the stages used by each integration will vary):
  • Account Type selection (e.g. Salesforce Production vs. Salesforce Sandbox) — AccountTypeStage
  • Pre-OAuth / API Key inputs (e.g. Stripe API Key input, Shopify Store URL) — PreOptionsStage
  • Post-OAuth inputs (e.g. Atlassian site, SharePoint site selection) — PostOptionsStage
Any redirection or popup window that is required for OAuth will automatically occur between stages. The SDK will internally manage all state for the popup window and OAuth prompt, but if an error occurs during the install flow, you can receive errors using the onError callback and prompt users of any issues. Begin an Install Flow by calling paragon.installFlow.start:
paragon.installFlow
  .start(props.integration, {
    onNext: (next) => {
      // Handle next install stage by presenting or updating UI for user input.
      // Possible stages include:
      // - AccountTypeStage
      // - PreOptionsStage
      // - PostOptionsStage
    },
    onComplete: () => {
      // Installation has completed successfully
    },
    onError: (error, errorContext) => {
      // Handle error messaging
    }
  });
After calling .start, you may receive required install stages in the onNext callback (depending on the specific install flow required by the integration). To handle each of the install stages in your app, you will need to:
  1. Receive the stage details in the onNext callback.
  2. Render the appropriate UI for your user to provide the necessary details in that stage.
    For the PostOptionsStage, refer to the User Settings docs below for help rendering integration-specific dropdown inputs.
  3. Update SDK state with the user’s inputs by calling one of:

Example Install Flow

Here’s a step-by-step example for how these stages would be implemented for Salesforce account types:
  1. Receive the AccountTypeStage in onNext handler:
    paragon.installFlow.start("salesforce", {
      onNext: (next) => {
        if (next.stage === "accountType") {
          setAccountTypes(next.stage.options);
          // [{ "id": "default", "accountDescription": "Production Account", "scheme": "oauth" }, {...}, {...}]
        }
      },
    });
    
  2. Render account type options for your user to select the available types:
    return accountTypes.map((accountType) => {
      return (
        <Button
          key={accountType.id}
        >
          {accountType.accountDescription}
        </Button>
      );
    });
    
    Account Types selector

    Example UI rendered by above code

  3. Update SDK state with the user’s inputs: Bind the onClick handler for the account type selection buttons to confirm the user’s selection and start the OAuth flow.
    return accountTypes.map((accountType) => {
      return (
        <Button
          key={accountType.id}
          onClick={() => paragon.installFlow.setAccountType(accountType.id)}
        >
          {accountType.accountDescription}
        </Button>
      );
    });
    
To implement the other Install Flows, check out our example implementation or SDK Reference:

Uninstalling Integrations

When a user requests to disconnect their account, call uninstallIntegration to remove their account:
async function onClickUninstall() {
  setUninstalling(true);
  await paragon.uninstallIntegration(props.integration);
  setUninstalling(false);
}
This function returns a Promise that resolves when the account has been fully disconnected from Paragon (including any workflows or settings they may have configured).

SDK Reference

Learn more about uninstallIntegration() in the SDK Reference.

User Settings and Workflow Configuration

After your user has connected an integration, show a Configuration screen with your configured User Settings to customize their integration, like a Salesforce Field Mapping or a Google Drive Folder to sync files from. If you are using Workflows and want your users to have controls to opt-in or out of specific Workflows, you can render toggles to enable or disable Workflows.
Google Drive User Settings

A configuration screen to customize the Google Drive integration with User Settings

You can use the getIntegrationConfig method to get all the User Settings that you have configured for this integration’s Connect Portal:
paragon.getIntegrationConfig(type);
User Settings will be available as the availableUserSettings key of the response.
Response
{
  "availableUserSettings": [
    {
      "id": "2d5662c9-6750-46c2-8588-2ac904532efb",
      "type": "DYNAMIC_ENUM",
      "title": "Channel",
      "required": false,
      "sourceType": "channels"
    }
  ],
  "shortDescription": "Send notifications to Slack",
  "longDescription": "Connect your Slack workspace to receive notifications and alerts in Slack. Stay connected to important activity by bringing it all together in your Slack workspace.\n\nOur Slack integration enables you to:\n\n• Receive alerts and notifications in your Slack workspace\n• Notify or DM specific team members based on certain activity",
  "availableWorkflows": [
    {
      "id": "2248335c-671c-47e4-b9a0-3641a9f2d301",
      "inputs": [],
      "infoText": "Send a Slack notification when a Task is created",
      "defaultEnabled": false,
      "description": "Send Slack Notification"
    }
  ],
  "hiddenWorkflows": []
}

Exposing User Settings

User Settings are inputs that appear during the install or configure stage of the Connect Portal, like the Folder input in the example above. Your implementation should render inputs when:
  • Your user is prompted with pre-OAuth options (PreOptionsStage)
  • Your user is prompted with post-OAuth options (PostOptionsStage)
    • For both PreOptionsStage and PostOptionsStage, User Settings objects can be found in stage.options.
  • Your user is configuring integration-level or workflow-level User Settings
    • User Settings objects can be found in getIntegrationConfig, under availableUserSettings and availableWorkflows[n].inputs.
Some User Settings (such as SidebarInputType.DynamicEnum or SidebarInputType.FieldMapping) will require you to render a dynamic list of options from the integration (a “data source”). You can use the following functions to help load this list:
  • getFieldOptions - Using a sourceType property from the User Setting object from getIntegrationConfig, load a page of options to show in the dropdown input.
    • For example, for a DynamicEnum input, call getFieldOptions to load a list of available options:
      Dynamic Enum input
      let config = paragon.getIntegrationConfig("slack");
      
      {
        "availableUserSettings": {
          "id": "2d5662c9-6750-46c2-8588-2ac904532efb",
          "type": "DYNAMIC_ENUM",
          "title": "Channel",
          "required": false,
          "sourceType": "channels"
        },
        // ...
      }
      
      await paragon.getFieldOptions({
        integration: "slack",
        action: "channels" // from `sourceType`
      });
      
      {
        "data": [
          { "label": "general", value: "general" },
          // ...
        ],
        "nextPageCursor": "...",
      }
      
  • getDataSourceOptions - For combination inputs and Field Mapping inputs, pass a sourceType property from the User Setting object to get all applicable data sources to render that input.
    • For example, for a FieldMapping input, call getDataSourceOptions to get the required keys needed to call getFieldOptions for both available Record Types to map and available fields for a selected Record Type:
      Field Mapping input
      let config = paragon.getIntegrationConfig("salesforce");
      
      {
        "availableUserSettings": {
          "id": "97ca5c16-887e-4047-96e3-50cf0541a17f",
          "type": "FIELD_MAPPER",
          "title": "Map fields for a Task",
          "required": true,
          "sourceType": "customObjectMapping",
          "useDynamicMapper": true,
          "savedFieldMappings": [...],
          "dynamicObjectName": "Task",
          "dynamicObjectOptions": {...}
        },
        // ...
      }
      
      await paragon.getDataSourceOptions(
        "salesforce",
        "customObjectMapping", // from `sourceType`
      );
      
      {
        "id": "customObjectMapping",
        "type": "FIELD_MAPPER_DATA_SOURCE",
        "title": "Field Mapping",
        "subtitle": "Allows users to define a field mapping",
        "recordSource": {
          "type": "DYNAMIC_DATA_SOURCE",
          "hideFromConnectFieldTypes": true,
          "title": "Record Type",
          "cacheKey": "recordTypes",
          "refreshDependencies": []
        },
        "fieldSource": {
          "type": "DYNAMIC_DATA_SOURCE",
          "hideFromConnectFieldTypes": true,
          "title": "Field",
          "cacheKey": "cachedFields",
          "refreshDependencies": ["recordType"],
        },
      }
      
      // Load Record Types:
      await paragon.getFieldOptions({
        integration: "salesforce",
        action: "recordTypes", // from `recordSource.cacheKey`
      });
      
      // Load Fields: 
      await paragon.getFieldOptions({
        integration: "salesforce",
        action: "cachedFields", // from `fieldSource.cacheKey`
        parameters: [{
          "key": "recordTypes",
          "source": {
            "type": "VALUE",
            "value": "Task"
          }
        }],
      });
      
      
You can see a full list of input types that are used by the Connect Portal and must be rendered by your implementation in Input Types Reference. Alternatively, you can start by using our React-based input renderer in our reference implementation. Once user input is collected, you can save the values back to Paragon using one of the following functions, depending on the input’s context:

Exposing Workflow Configuration

Workflow Configuration example

Optionally, show a list of Workflows for users to enable / disable in the Connect Portal

In the Configuration tab of the headful Connect Portal, a list of Workflows appears as a list of toggles that users can opt-in or out of. This is not required if you do not use Workflows are your Workflows are set to be enabled by default.
  • To render the list of available Workflows, you can use getIntegrationConfig and read the availableWorkflows key.
  • To see what Workflows the user has enabled, you can use getUser and read user.integrations[].configuredWorkflows[].enabled.
    Getting Workflow state
    paragon.getUser();
    
    // Response:
    {
      "integrations": {
        "slack": {
          // (other properties excluded for brevity)
          "configuredWorkflows": {
            "7b516f0f-2581-470f-a26d-72dfa7b9b554": {
              "enabled": false,
              "settings": {}
            }
          }
        }
      }
    }
    
  • To toggle a Workflow on or off for a user, you can use updateWorkflowState to pass a partial update object of the Workflows that should be enabled or disabled for a user.
    Updating Workflow state
    paragon.updateWorkflowState({
      // [Workflow UUID]: boolean
      "7b516f0f-2581-470f-a26d-72dfa7b9b554": true,
    });
    

Multiple Account Authorization

When implementing the Headless Connect Portal for Multiple Account Authorization, you’ll need to update your UI to handle listing multiple connections for a given integration. You can use selectedCredentialId in the following SDK functions to ensure that the right account is used in multi-account setups:
  • updateIntegrationUserSettings
    paragon.updateIntegrationUserSettings("googledrive", {
      [selectedInput.id]: newValue,
    }, {
      selectedCredentialId: "376910a0-74aa-4e5e-88ab-122a7fb20a56",
    });
    
  • updateWorkflowUserSettings
    paragon.updateWorkflowUserSettings("googledrive", selectedWorkflow.id, {
      [selectedInput.id]: newValue,
    }, {
      selectedCredentialId: "376910a0-74aa-4e5e-88ab-122a7fb20a56",
    });
    
  • updateWorkflowState
    paragon.updateWorkflowState({
      "7b516f0f-2581-470f-a26d-72dfa7b9b554": true,
    }, {
      selectedCredentialId: "376910a0-74aa-4e5e-88ab-122a7fb20a56",
    });
    
  • uninstallIntegration
    paragon.uninstallIntegration("googledrive", {
      selectedCredentialId: "376910a0-74aa-4e5e-88ab-122a7fb20a56",
    });
    
In all of the above, you can get the Credential ID for an account from getUser(), in user.integrations[].allCredentials[n].id.

SDK Reference

The SDK includes the key functions below that should be used when implementing the Headless Connect Portal. Find more details about each function (including parameter types and return values) in the linked reference page.
I