> ## Documentation Index
> Fetch the complete documentation index at: https://docs.safeclose.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Link bank accounts with Plaid in Safeclose

> Use Plaid Link inside the Safeclose signing flow to let users verify and connect their bank accounts securely, then check connection status at any time.

Safeclose uses Plaid to let signers verify and link their bank accounts as part of the signing flow. When a signer connects their bank, Safeclose retrieves a short-lived token on their behalf, passes it through the secure Plaid Link UI, and exchanges it for a persistent item reference — no raw bank credentials ever touch your application.

You can try the Plaid Link flow interactively in the Safeclose signing lab at **`/signing/plaid-lab`**.

<Note>
  Which Plaid environment is active (sandbox or production) is configured by your workspace administrator. In sandbox mode, use Plaid's [test credentials](https://plaid.com/docs/sandbox/test-credentials/) to complete the flow without a real bank account.
</Note>

## How the Plaid Link flow works

<Steps>
  <Step title="Request a link token">
    Call `POST /api/plaid/link-token` from your signed-in session. Safeclose returns a short-lived `link_token` scoped to the current user.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://your-app.com/api/plaid/link-token \
        -H "Cookie: <your-session-cookie>"
      ```

      ```json Response theme={null}
      {
        "link_token": "link-sandbox-abc123..."
      }
      ```
    </CodeGroup>

    The `link_token` expires quickly — pass it to the Plaid Link UI immediately.
  </Step>

  <Step title="Initialize Plaid Link">
    Use the `link_token` to open the Plaid Link UI. In a React app, pass the token to `usePlaidLink` from the [`react-plaid-link`](https://github.com/plaid/react-plaid-link) package:

    ```javascript theme={null}
    import { usePlaidLink } from 'react-plaid-link';

    const { open, ready } = usePlaidLink({
      token: linkToken,   // from POST /api/plaid/link-token
      onSuccess: (publicToken, metadata) => {
        // exchange the public_token on your server
      },
    });
    ```

    The user selects their institution and authenticates inside the Plaid Link modal. Your app code never handles credentials directly.
  </Step>

  <Step title="Exchange the public token">
    After the user completes Plaid Link, Plaid calls your `onSuccess` callback with a one-time `public_token`. Send it to `POST /api/plaid/exchange` to exchange it for a persistent item reference.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://your-app.com/api/plaid/exchange \
        -H "Cookie: <your-session-cookie>" \
        -H "Content-Type: application/json" \
        -d '{ "public_token": "public-sandbox-abc123..." }'
      ```

      ```json Response theme={null}
      {
        "item_id": "eVBnVMp7zxNmBoTgB3K",
        "institution_id": "ins_3",
        "persisted": true,
        "persist_hint": null
      }
      ```
    </CodeGroup>

    | Field            | Description                                                                                                   |
    | ---------------- | ------------------------------------------------------------------------------------------------------------- |
    | `item_id`        | Plaid's stable identifier for the linked bank item.                                                           |
    | `institution_id` | Plaid's identifier for the financial institution.                                                             |
    | `persisted`      | `true` when the connection was saved to your account.                                                         |
    | `persist_hint`   | Non-null message if the connection could not be saved (ask your admin to check encryption key configuration). |

    <Warning>
      Call `POST /api/plaid/exchange` from your server-side code or immediately after `onSuccess`. The `public_token` can only be exchanged once and expires after 30 minutes.
    </Warning>
  </Step>

  <Step title="Check link status">
    At any time, call `GET /api/plaid/status` to check whether the signed-in user has a linked Plaid item.

    <CodeGroup>
      ```bash cURL theme={null}
      curl https://your-app.com/api/plaid/status \
        -H "Cookie: <your-session-cookie>"
      ```

      ```json Response — connected theme={null}
      {
        "connected": true,
        "itemId": "eVBnVMp7zxNmBoTgB3K",
        "institutionId": "ins_3"
      }
      ```

      ```json Response — not connected theme={null}
      {
        "connected": false,
        "itemId": null,
        "institutionId": null
      }
      ```
    </CodeGroup>

    Use `connected: false` to prompt the user to complete the Plaid Link flow.
  </Step>
</Steps>

## API summary

| Method | Path                    | Auth             | Description                                                                                |
| ------ | ----------------------- | ---------------- | ------------------------------------------------------------------------------------------ |
| `POST` | `/api/plaid/link-token` | Session required | Returns a `link_token` to initialize Plaid Link.                                           |
| `POST` | `/api/plaid/exchange`   | Session required | Exchanges a `public_token` for a stored item reference. Body: `{ "public_token": "..." }`. |
| `GET`  | `/api/plaid/status`     | Session required | Returns whether the current user has a linked Plaid item.                                  |

## Plaid webhook events

Plaid sends item update notifications to Safeclose automatically — for example, when new transactions are available or when a linked item needs re-authentication. These events arrive at `POST /api/webhooks/plaid` and are processed asynchronously.

<Card title="Webhook configuration" icon="webhook" href="/integrations/webhooks">
  Learn how Safeclose verifies Plaid webhook events and what your administrator needs to configure.
</Card>
