Versa Link Integration Guide

Versa Link is the client-side component that your users will interact with in order to link their accounts to your app. It handles all aspects of the account linking experience.

A screenshot of Versa Link on an example website.

Versa Link is one method of customer registration, in which most of the work is handled via the embeddable widget. Alternatively, you are welcome to implement your own customer registration via the Versa API.

IMPORTANT

The only way to support OAuth-based connections (including Brex, QuickBooks, and Xero) is via Versa Link. The customer registration API does not support OAuth connections. Versa Link is the best way to ensure the broadest compatibility.

The Link flow begins when your user wants to connect their expense management account or duty-of-care account to your app.

  1. Generate a link token and pass the temporary token to your app’s client.
  2. Use the link token to initialize the Versa Link widget.
  3. Launch the widget by calling the open method.
  4. The user will select the account they want to connect.
  5. Your app will receive the account details via the onSuccess callback.

Installation

Drop the script tag into your HTML to include the Versa Link widget.

<script src="https://cdn.versa.org/link/stable/versa-link.iife.js"></script>

CSP directives

If you make use of a Content Security Policy (CSP), the following directives will allow Link traffic:

default-src https://cdn.versa.org/;
script-src 'unsafe-inline' https://cdn.versa.org/link/stable/versa-link.iife.js;
connect-src https://cdn.versa.org/ https://registry.versa.org/ https://custodial.versa.org/;

If your organization does not allow the use of unsafe-inline, you can use a CSP nonce instead.

First, make an account at https://app.versa.org and issue yourself test credentials.

Before you can initialize Versa Link, you need to create a link token. Use your server-side credentials to acquire a short-lived token to pass to the client (tokens expire after 10 minutes).

In production, you will need to create a secure endpoint on your server that generates this token and returns it to your client application on behalf of the logged-in customer.

curl -X POST https://registry.versa.org/link_token \
    -H "Authorization: Basic {CLIENT_ID}:{CLIENT_SECRET}" \
    -H "Content-Type: application/json" \
    -d '{
      "handles": {
        "customer_email_domain": "acme.co",
        "merchant_group_code": "CORPORATE_DISCOUNT_CODE_XYZ"
      }
    }'

This will return a response object including the following fields:

{
  "token": "link_test_wnYq4f2YjWsvHq4ffI2FYydOMQjMmJVpadUFlBWd",
  "mode": "test",
  "expired": 1754020800
}

Call VersaLink.initialize() to configure the widget with the token string provided in the response from the /link_token endpoint above.

await VersaLink.initialize({
  linkToken: data.token,
  onSuccess: () => {},
  onExit: () => {},
});

Launch the Widget

Once initialized, you can launch the Versa Link widget by calling the open method.

VersaLink.open();

Link can be customized in a few different ways:

FieldTypeDescription
themenullable objectSpecify available themes options.
whitelistOrgsnullable arrayAn explicit list of integrations to offer. If not provided, the default set of integrations will be offered.
languagenullable stringLanguage code for the widget UI (e.g., en, es, fr). Defaults to en. Controls the display language for all text in the widget interface.

Theme

If you would like to customize the appearance of the Versa Link widget, you can do so by passing an optional theme object to the initialize method.

FieldTypeDescription
viewModeenumOne of modal or inline. Defaults to modal. See below for a comparison of the modal and inline themes.

A comparison of the 'modal' and 'inline' themes.

IMPORTANT

For the 'inline' view mode, you must include a container element in your HTML with the id versa-canvas. The Versa Link script will query for this element by id and render the widget inside. See example below:

index.html
<body>
  <div id="versa-canvas">
    <!-- Versa Link widget will render inside this canvas -->
  </div>
  <script src="https://cdn.versa.org/link/stable/versa-link.iife.js"></script>
  <script>
    (async function initInline() {
      const response = await fetch("/api/link-token");
      await VersaLink.initialize({
        linkToken: data.token,
        theme: {
          viewMode: "inline",
        },
      });

      // Immediately open; widget will render into #versa-canvas
      VersaLink.open();
    })();

  </script>
</body>

If there are additional theming customizations you would like to make, please reach out to the Versa team.

Whitelist Orgs

By default, Versa Link will offer all available integrations to the user. If you would like to limit the integrations that are offered, you can do so by passing an optional whitelistOrgs array to the initialize method. Reach out to the Versa team if you would like help identifying the org ids of the integrations you would like to offer.

Language

CAUTION

Localization is a beta feature. See a bad translation? Let us know at support@versa.org.

Link currently supports the following languages:

CodeLanguage
enEnglish
daDanish
deGerman
esSpanish
etEstonian
frFrench
hiHindi
itItalian
ltLithuanian
lvLatvian
nlDutch
noNorwegian
plPolish
ptPortuguese
roRomanian
svSwedish
viVietnamese

Response Handling

When a user completes a connection, the onSuccess callback receives a response object. This object contains information about the changes that occurred during the Link session, and the current state of all connections.

onSuccess Example
VersaLink.initialize({
  linkToken: data.token,
  onSuccess: (res) => {
    res.changes.forEach((change) => {
      if (change.action === "enabled") {
        console.log(`Connected to ${change.org.name}`);
      } else {
        console.log(`Disconnected from ${change.org.name}`);
      }
    });
    console.log(`Total active connections: ${res.connections.length}`);
  },
  ...
});

The response object includes changes and connections:

{
  "changes": [...],
  "connections": [...]
}
PropertyTypeDescription
changesarrayWhat changed in this Link session. See change object definition.
connectionsarrayAll currently active connections. See connection object definition.
Change Object

Change Object

Describes a single connection change that occurred during the session.

PropertyTypeDescription
orgobjectA partial org object describing the receiver organization.
oauthbooleanWhether this connection uses OAuth for setting up the connection.
categoryenumThe category of the receiver: expense, firehose, or duty_of_care.
actionenumThe action performed on this connection: enabled or disabled.
Connection Object

Connection Object

Represents an active connection to a receiver organization.

PropertyTypeDescription
orgobjectA partial org object describing the receiver organization.
oauthbooleanWhether this connection uses OAuth for setting up the connection.
categoryenumThe category of the receiver: expense, firehose, or duty_of_care.
handlingobjectA handling object describing the registration status information.
Partial Org Object

PartialOrg

PropertyTypeDescription
idstringUnique identifier for the organization.
slugstringURL-friendly organization identifier.
namestringDisplay name of the organization.
websitestringOrganization's website URL.
logostringURL to the organization's logo.
Handling Object

Handling

PropertyTypeDescription
receiver_registeredbooleanWhether registered by the receiver.
sender_registeredbooleanWhether registered by the sender.

Versa periodically updates Link to add new functionality and improve conversion. These changes will be automatically deployed. Any test suites and business logic in your app should be robust to the possibility of changes to the user-facing Link flow. Any major changes, especially those related to the inline view mode, will be shared with partners well in advance of deployment.