Skip to Content

Quickstart

Get up and running with the Web / React SDK in 5 minutes.

Prerequisites

  • React 16.8+ (hooks support)
  • A Passage Connect API key

Step 1: Install

npm install @getpassage/web-react

Step 2: Add PassageProvider

Wrap your app (or the relevant subtree) with PassageProvider. This renders the App Clip modal and manages WebSocket connections.

import { PassageProvider } from '@getpassage/web-react'; function App() { return ( <PassageProvider> <MyComponent /> </PassageProvider> ); }

Your backend creates a link via the Connect API and returns the claimCode to the frontend.

curl -X POST https://connect.services.getpassage.ai/v1/links \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "integrationId": "tmobile", "resource": "mobileBillingStatement", "action": "read", "webhookUrl": "https://your-server.com/webhook" }'

The response includes a claimCode (e.g. clm_xxx). Return this to your frontend.

// Example Node.js backend route app.post('/api/create-link', async (req, res) => { const response = await fetch('https://connect.services.getpassage.ai/v1/links', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.PASSAGE_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ integrationId: 'tmobile', resource: 'mobileBillingStatement', action: 'read', webhookUrl: 'https://your-server.com/webhook', }), }); const link = await response.json(); res.json({ claimCode: link.claimCode }); });

Step 4: Open the App Clip

Use the usePassage hook to call openAppClip with the claim code. The SDK shows a QR code modal and monitors the link status via WebSocket.

import { usePassage } from '@getpassage/web-react'; function MyComponent() { const { openAppClip } = usePassage(); const handleConnect = async () => { const res = await fetch('/api/create-link', { method: 'POST' }); const { claimCode } = await res.json(); openAppClip({ claimCode, onConnectionComplete: () => { console.log('Account connected!'); }, onConnectionError: (error) => { console.error('Connection failed:', error.error); }, }); }; return <button onClick={handleConnect}>Connect Account</button>; }

Full example

import React, { useState } from 'react'; import { PassageProvider, usePassage } from '@getpassage/web-react'; import type { PassageErrorData } from '@getpassage/web-react'; function App() { return ( <PassageProvider> <ConnectAccount /> </PassageProvider> ); } function ConnectAccount() { const { openAppClip } = usePassage(); const [status, setStatus] = useState(''); const [isLoading, setIsLoading] = useState(false); const connectAccount = async () => { setIsLoading(true); setStatus(''); try { const res = await fetch('/api/create-link', { method: 'POST' }); const { claimCode } = await res.json(); openAppClip({ claimCode, onConnectionComplete: () => { setStatus('Connected successfully!'); setIsLoading(false); }, onConnectionError: (error: PassageErrorData) => { setStatus(`Failed: ${error.error}`); setIsLoading(false); }, }); } catch (error) { setStatus(`Error: ${error instanceof Error ? error.message : 'Unknown'}`); setIsLoading(false); } }; return ( <div> <button onClick={connectAccount} disabled={isLoading}> {isLoading ? 'Connecting...' : 'Connect Account'} </button> {status && <p>{status}</p>} </div> ); } export default App;

Next steps

Last updated on