Navigation

Link Integration Guide

Connect your users to their biller accounts in 3 steps: create a link token, open the Connect SDK, and exchange the token for persistent access.

1

Create a Link Token

Your server creates a short-lived link token that identifies the user and the biller they want to connect. Send this token to your frontend to initialize the Connect SDK.

POST /link/token/create
const response = await fetch('https://sandbox.api.billerapi.com/link/token/create', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Client-ID': process.env.BILLERAPI_CLIENT_ID,
    'X-Client-Secret': process.env.BILLERAPI_CLIENT_SECRET,
  },
  body: JSON.stringify({
    client_id: process.env.BILLERAPI_CLIENT_ID,
    client_user_id: 'user_123',
    biller_id: 'test_electric_company',
    consents: ['bills'],
  }),
});
const { link_token } = await response.json();
// Send link_token to your frontend

Request body

JSON
{
  "client_id": "string",
  "client_user_id": "string",
  "biller_id": "string (optional)",
  "consents": ["bills"]
}

Response

JSON
{
  "success": true,
  "link_token": "string",
  "expires_at": "string"
}

Note

Link tokens expire after 30 minutes. Create a new one for each linking session — do not cache or reuse them.
2

Open the Connect SDK

Pass the link token to the Connect SDK on your frontend. The SDK handles credential entry, account discovery, and consent — then returns a public token in the onSuccess callback.

Initialize and open Connect
JavaScript
import { BillButler } from '@billerapi/connect-sdk';

const billbutler = new BillButler({
  clientId: 'your_client_id',
  environment: 'sandbox',
});

const handler = billbutler.create({
  linkToken: linkToken, // from your server
  onSuccess: async (publicToken, metadata) => {
    console.log('Account linked!', metadata.institutionName);

    // Exchange the public token on your server
    const res = await fetch('/api/exchange-token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ publicToken }),
    });
    const { access_token } = await res.json();
    // Store access_token — you'll need it to fetch bills
  },
  onExit: (error) => {
    if (error) console.error('Error:', error.code);
  },
});

handler.open();

React component example

React
import React, { useState, useCallback, useRef } from 'react';
import { BillButler } from '@billerapi/connect-sdk';

const LinkButton = ({ userId, onSuccess }) => {
  const [isLoading, setIsLoading] = useState(false);
  const billbutlerRef = useRef(
    new BillButler({
      clientId: process.env.NEXT_PUBLIC_BILLERAPI_CLIENT_ID,
      environment: 'sandbox',
    })
  );

  const handleLink = useCallback(async () => {
    setIsLoading(true);
    try {
      // Get link token from your server
      const response = await fetch('/api/create-link-token', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ userId })
      });
      const { linkToken } = await response.json();

      // Open Connect SDK
      const handler = billbutlerRef.current.create({
        linkToken,
        onSuccess: async (publicToken, metadata) => {
          await fetch('/api/exchange-token', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ publicToken }),
          });
          onSuccess?.(metadata);
        },
        onExit: (error) => {
          if (error) console.error(error.code);
          setIsLoading(false);
        },
      });
      handler.open();
    } catch (error) {
      console.error('Error creating link token:', error);
      setIsLoading(false);
    }
  }, [userId, onSuccess]);

  return (
    <button
      onClick={handleLink}
      disabled={isLoading}
      className="bg-primary text-primary-foreground px-6 py-3 rounded-lg"
    >
      {isLoading ? 'Connecting...' : 'Link Account'}
    </button>
  );
};

export default LinkButton;

Sandbox test credentials

Use account number 4242424242 with any username and password. This always succeeds. See the Getting Started guide for the full sandbox reference table.
3

Exchange the Public Token

Send the public token from the onSuccess callback to your server, then exchange it for a long-lived access token. Store the access token securely — you'll use it for all subsequent API calls on this linked account.

POST /link/token/exchange
const response = await fetch('https://sandbox.api.billerapi.com/link/token/exchange', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Client-ID': process.env.BILLERAPI_CLIENT_ID,
    'X-Client-Secret': process.env.BILLERAPI_CLIENT_SECRET,
  },
  body: JSON.stringify({
    client_id: process.env.BILLERAPI_CLIENT_ID,
    public_token: publicToken,
  }),
});
const { access_token, biller_id, link_id } = await response.json();
// Store access_token securely — use it to fetch bills

Request body

JSON
{
  "client_id": "string",
  "public_token": "string"
}

Response

JSON
{
  "success": true,
  "access_token": "string",
  "biller_id": "string",
  "link_id": "string"
}

Note

Public tokens are single-use and expire after 30 minutes. Always exchange them immediately after receiving the onSuccess callback.

Related