# Plaid Integration

> Connect bank accounts and sync transactions via Plaid.

- **Category:** Integrations
- HTML version: https://www.ondayzero.com/docs/guides/plaid
- All guides: https://www.ondayzero.com/docs/guides/index.md
- Endpoint catalog: https://www.ondayzero.com/docs/reference/index.md

---

# Plaid Integration

Connect bank accounts through Plaid for automatic transaction syncing.

> **Where this fits in the API.** The easiest way to connect a bank is the **Integrations** page in the DayZero dashboard, which drives the flow below for you. The `/api/v1/plaid/*` endpoints are callable with an API token but are *not* part of the public API Reference — they exist to support DayZero's own UI and may change. Once a bank is connected, its data appears through the public [Transactions](/docs/guides/transactions) and [Ledgers](/docs/guides/ledgers) endpoints, which is where integrations should read from.

## Connect a Bank Account

There are two flows. **Hosted Link** needs no frontend code: DayZero returns a Plaid-hosted URL (and emails it to the recipient) where the account holder logs in to their bank.

```bash
curl -X POST "https://api.ondayzero.com/api/v1/plaid/hosted-link" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID" \
  -H "Content-Type: application/json" \
  -d '{ "email": "owner@acme.com", "completion_redirect_uri": "https://app.example.com/connected" }'
```

Plaid's webhook completes the connection; poll `GET /api/v1/plaid/pending-connection-requests` and `GET /api/v1/plaid/connections` to see it arrive.

**Embedded Link** is for your own UI using `react-plaid-link` (or another Plaid Link SDK):

1. Mint a Link token — the response has a `link_token` for the SDK and a `connection_request_id` to echo back:

   ```bash
   curl -X POST "https://api.ondayzero.com/api/v1/plaid/link-token" \
     -H "Authorization: Bearer dz_your_token_here" \
     -H "x-business-id: YOUR_BUSINESS_ID" \
     -H "Content-Type: application/json" \
     -d '{}'
   ```

2. Open Plaid Link with the `link_token`.
3. In the SDK's `onSuccess` callback, exchange the public token:

   ```bash
   curl -X POST "https://api.ondayzero.com/api/v1/plaid/exchange-public-token" \
     -H "Authorization: Bearer dz_your_token_here" \
     -H "x-business-id: YOUR_BUSINESS_ID" \
     -H "Content-Type: application/json" \
     -d '{
       "public_token": "public-sandbox-...",
       "connection_request_id": "CONNECTION_REQUEST_UUID",
       "institution_id": "ins_3",
       "institution_name": "Chase"
     }'
   ```

## Choose Which Accounts to Sync

A connection can expose several accounts (checking, savings, a card). Pick the ones to sync and, optionally, map each to an existing ledger or set how far back to import:

```bash
curl -X POST "https://api.ondayzero.com/api/v1/plaid/link-selected-accounts" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "connection_id": "CONNECTION_UUID",
    "plaid_account_ids": ["PLAID_ACCOUNT_ID"],
    "entries_start": "2026-01-01"
  }'
```

Each linked account becomes a bank ledger (with `financial_account_type: bank_account` or `credit_card`) that transactions flow into. To reuse an existing ledger instead of creating a new one, list the eligible ones with `GET /api/v1/plaid/ledger-candidates?plaid_account_type=depository` (`depository` → asset ledgers; `credit` or `loan` → liability ledgers) and pass `"ledger_assignments": {"PLAID_ACCOUNT_ID": "LEDGER_UUID"}` when linking. Ledgers with a reconciliation in progress cannot be changed until it completes.

## Transaction Syncing

Once connected, transactions sync automatically. New transactions are:

1. Imported with raw bank descriptions (`source: plaid`)
2. Cleaned (merchant name normalization into `counterparty`)
3. Auto-categorized using AI and user-defined bank rules
4. Available for review in `GET /api/v1/transactions` — see [Transactions](/docs/guides/transactions)

`POST /api/v1/plaid/refresh-balances` refreshes cached account balances on demand.

## Reauthorization

Bank connections occasionally require reauthorization (e.g., when a bank requires re-login or MFA). DayZero tracks these events — the connection's `sync_status` and `error_code` show it in `GET /api/v1/plaid/connections` — and prompts users in the dashboard. Programmatically, `POST /api/v1/plaid/reauthorize-connection` with `{"connection_id": "…"}` returns a fresh Hosted Link URL, or pass `connection_id` to `/plaid/link-token` for an update-mode embedded token.

## List Connected Accounts

```bash
curl "https://api.ondayzero.com/api/v1/plaid/connections" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID"
```

Returns every bank connection with its institution, status and accounts. `GET /api/v1/plaid/bank-accounts` lists the linked accounts with their cached balances. The same connection summary is embedded on each business as `plaid_connections` in `GET /api/v1/businesses`.

## Disconnect

Preview the impact first — unlinking stops syncing and can affect ledgers that only exist because of the feed:

```bash
curl -X POST "https://api.ondayzero.com/api/v1/plaid/account/unlink-preview" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID" \
  -H "Content-Type: application/json" \
  -d '{"connection_id": "CONNECTION_UUID", "plaid_account_id": "PLAID_ACCOUNT_ID"}'
```

Then `POST /api/v1/plaid/account/unlink` with the same body (plus `confirm_phrase` — the linked ledger's exact name — when the preview reports linked ledgers) to stop syncing one account, or `DELETE /api/v1/plaid/connection` with `{"connection_id": "…"}` to remove the whole bank connection (`POST /api/v1/plaid/connection/delete-preview` first). Already-imported transactions stay in the books.
