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

# Widget-only Best Practices

> Guidelines for implementing Deposit Switch and Paycheck Linked Lending

Follow these best practices for Deposit Switch (DDS) and Paycheck Linked Lending (PLL) integrations using the Widget-only flow.

***

## Wait for webhooks before proceeding

Always wait for the `task-status-updated` webhook with `status: done` before treating a connection as complete. Do not rely solely on the client-side `onSuccess` callback. The task may still be processing server-side.

```bash theme={null}
# When you receive a task-status-updated webhook with status: done,
# retrieve the link details
curl --request GET \
     --url https://prod.truv.com/v1/links/{link_id}/ \
     --header 'X-Access-Client-Id: YOUR_TRUV_CLIENT_ID' \
     --header 'X-Access-Secret: YOUR_TRUV_CLIENT_SECRET'
```

***

## Validate account details upfront

Provide complete and accurate bank account information when creating the bridge token. Missing or incorrect fields cause configuration failures.

**Deposit Switch**: route the user's entire paycheck or a fixed amount:

```bash theme={null}
curl --request POST \
     --url https://prod.truv.com/v1/users/USER_ID/tokens/ \
     --header 'X-Access-Client-Id: YOUR_TRUV_CLIENT_ID' \
     --header 'X-Access-Secret: YOUR_TRUV_CLIENT_SECRET' \
     --header 'Content-Type: application/json' \
     --data '{
  "product_type": "deposit_switch",
  "account": {
    "account_number": "16002600",
    "routing_number": "123456789",
    "bank_name": "TD Bank",
    "account_type": "checking",
    "deposit_type": "entire"
  }
}'
```

**Paycheck Linked Lending**: deduct a fixed amount per paycheck:

```bash theme={null}
curl --request POST \
     --url https://prod.truv.com/v1/users/USER_ID/tokens/ \
     --header 'X-Access-Client-Id: YOUR_TRUV_CLIENT_ID' \
     --header 'X-Access-Secret: YOUR_TRUV_CLIENT_SECRET' \
     --header 'Content-Type: application/json' \
     --data '{
  "product_type": "pll",
  "account": {
    "account_number": "16002600",
    "routing_number": "123456789",
    "bank_name": "TD Bank",
    "account_type": "checking",
    "deposit_type": "amount",
    "deposit_value": "200.00"
  }
}'
```

### Deposit type options

| `deposit_type` | Description                                              |
| -------------- | -------------------------------------------------------- |
| `entire`       | Route the entire paycheck                                |
| `amount`       | Route a fixed dollar amount (set `deposit_value`)        |
| `percent`      | Route a percentage of the paycheck (set `deposit_value`) |

***

## Handle errors from webhooks

Monitor `task-status-updated` webhooks for error statuses. These indicate the connection or configuration failed.

| Webhook status   | Cause                                            | Resolution                                                                                                        |
| ---------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
| `login_error`    | Incorrect credentials                            | Prompt the user to re-authenticate via [Bridge Update Mode](/developers/integration/bridge-widget/returning-user) |
| `mfa_error`      | MFA challenge failed                             | Prompt the user to try again                                                                                      |
| `config_error`   | Provider doesn't support the requested operation | Fall back to manual instructions or try a different provider                                                      |
| `account_locked` | Too many failed attempts at the provider         | Ask the user to unlock their account with the provider                                                            |

See [Task lifecycle](/api-reference/tasks/lifecycle) for all status transitions.

***

## Handle client-side errors

Listen for `ERROR` events in the Bridge `onEvent` callback. The error object uses `error_code`, not a generic `code` field.

```javascript theme={null}
onEvent: function(type, payload) {
  if (type === 'ERROR') {
    switch (payload.error.error_code) {
      case 'LOGIN_ERROR':
        // User entered wrong credentials — Bridge handles retry UI
        break;
      case 'UNAVAILABLE':
        showMessage('This provider is temporarily unavailable. Try again later.');
        break;
      case 'LINK_EXISTS':
        showMessage('This account is already connected.');
        break;
    }
  }
}
```

See [Bridge Events](/developers/sdks/bridge-events#errors) for all error codes and their meanings.

***

## Use deeplinking when you know the employer

If you know the user's employer or payroll provider, pass it when creating the bridge token to skip search screens and improve conversion.

```bash theme={null}
curl --request POST \
     --url https://prod.truv.com/v1/users/USER_ID/tokens/ \
     --header 'X-Access-Client-Id: YOUR_TRUV_CLIENT_ID' \
     --header 'X-Access-Secret: YOUR_TRUV_CLIENT_SECRET' \
     --header 'Content-Type: application/json' \
     --data '{
  "product_type": "deposit_switch",
  "company_mapping_id": "48427a36d43c4d5aa6324bc06c692456",
  "account": {
    "account_number": "16002600",
    "routing_number": "123456789",
    "bank_name": "TD Bank",
    "account_type": "checking",
    "deposit_type": "entire"
  }
}'
```

See [Deeplinking](/developers/integration/bridge-widget/deeplinking) for the full guide.

***

## Check DDS/PLL support before connecting

Use the [company search endpoint](/api-reference/companies/company_autocomplete_search) to check if a provider supports DDS or PLL before routing the user through the flow. This avoids dead-end experiences where the user connects but the provider doesn't support the desired product.

```bash theme={null}
curl --request GET \
     --url 'https://prod.truv.com/v1/company-mappings-search/?query=Facebook' \
     --header 'X-Access-Client-Id: YOUR_TRUV_CLIENT_ID' \
     --header 'X-Access-Secret: YOUR_TRUV_CLIENT_SECRET'
```

Check the response for DDS/PLL availability before initiating the bridge token.

***

## Leverage VOIE-PLL flow

For combined income verification and paycheck linked lending, complete the VOIE flow first, then check if the connected provider supports DDS/PLL. After VOIE completes, fetch the link details:

```bash theme={null}
curl --request GET \
     --url https://prod.truv.com/v1/links/{link_id}/ \
     --header 'X-Access-Client-Id: YOUR_TRUV_CLIENT_ID' \
     --header 'X-Access-Secret: YOUR_TRUV_CLIENT_SECRET'
```

Check the `is_dds_available` flag in the response. If `true`, the user's payroll provider supports DDS/PLL and you can offer it as a follow-up step without requiring the user to reconnect.

***

## Understand the token flow

The Widget-only flow uses a three-token exchange to securely connect users to their payroll providers:

```text theme={null}
bridge_token → public_token → access_token
```

| Token          | Created By               | Lifetime    | Purpose                              |
| -------------- | ------------------------ | ----------- | ------------------------------------ |
| `bridge_token` | Your server (via API)    | 6 hours     | Initialize Truv Bridge on the client |
| `public_token` | Truv Bridge (on success) | Short-lived | Temporary token for exchange         |
| `access_token` | Token exchange (via API) | Persistent  | Configure DDS/PLL and retrieve data  |

## Store and protect tokens

* **`access_token`** is persistent and grants access to the user's data. Store it encrypted on your server.
* **`bridge_token`** is short-lived (6 hours) and safe to pass to the client. Generate a new one if the user hasn't started within that window.
* **`public_token`** is single-use. Exchange it immediately for an `access_token` via `POST /v1/link-access-tokens/` and discard it.

Never expose `access_token` or API credentials in client-side code.

***

## Verify webhook signatures

Always verify the `X-WEBHOOK-SIGN` header on incoming webhooks to confirm they are from Truv. Use the raw request body. Never use parsed JSON.

See [Webhook Security](/api-reference/security) for verification examples.

***

## Next steps

<CardGroup cols={2}>
  <Card title="Widget-only Integration" icon="key" href="/developers/integration/bridge-widget/new-user">
    Implementation guide for Widget-only flow
  </Card>

  <Card title="Returning User" icon="arrows-rotate" href="/developers/integration/bridge-widget/returning-user">
    Reconnect or update an existing link
  </Card>

  <Card title="Bridge Events" icon="bolt" href="/developers/sdks/bridge-events">
    Client-side event types and error codes
  </Card>

  <Card title="Task Lifecycle" icon="arrows-spin" href="/api-reference/tasks/lifecycle">
    Server-side connection statuses
  </Card>
</CardGroup>
