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

# Integrate the Deposit SDK

> Install and configure the Deposit SDK to trigger deposit flows from your frontend.

The Deposit SDK (`@swype-org/deposit`) opens Blink's hosted deposit flow in a modal iframe overlay. On mobile viewports (480px or narrower), the iframe renders full-screen for an app-like experience. The SDK handles the signer call, iframe lifecycle, passkey/WebAuthn delegation, and completion detection via `postMessage`.

## Install

<CodeGroup>
  ```bash npm theme={null}
  npm install @swype-org/deposit
  ```

  ```bash yarn theme={null}
  yarn add @swype-org/deposit
  ```

  ```bash pnpm theme={null}
  pnpm add @swype-org/deposit
  ```
</CodeGroup>

## JavaScript

```typescript theme={null}
import { Deposit, DepositError, getDisplayMessage } from '@swype-org/deposit';

const deposit = new Deposit({
  signer: '/api/sign-payment',
});

document.getElementById('transfer-btn')!.addEventListener('click', async () => {
  try {
    const { transfer: result } = await deposit.requestDeposit({
      amount: 50,
      chainId: 8453,
      address: '0x1a5FdBc891c5D4E6aD68064Ae45D43146D4F9f3a',
      token: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
    });
    console.log('Transfer complete:', result.id, result.status);
  } catch (error) {
    if (error instanceof DepositError) {
      showErrorToUser(getDisplayMessage(error));
    }
  }
});
```

## React hook

```tsx theme={null}
import { useBlinkDeposit } from '@swype-org/deposit/react';

function TransferButton() {
  const { status, result, error, displayMessage, requestDeposit } = useBlinkDeposit({
    signer: '/api/sign-payment',
  });

  const handleTransfer = () => {
    requestDeposit({
      amount: 50,
      chainId: 8453,
      address: userWalletAddress,
      token: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
    });
  };

  return (
    <>
      <button onClick={handleTransfer} disabled={status === 'signer-loading'}>
        {status === 'signer-loading' ? 'Preparing...' : 'Deposit $50'}
      </button>
      {error && <p className="error">{displayMessage}</p>}
      {result && <p>Transfer {result.transfer.id} complete!</p>}
    </>
  );
}
```

The hook returns reactive `status`, `result`, `error`, `displayMessage`, and `isActive` values, plus `requestDeposit`, `focus`, and `close` actions. It manages the `Deposit` lifecycle and cleans up on unmount.

## Prebuilt deposit button

You don't have to design your own button. The SDK ships `BlinkDepositButton`, a prebuilt Blink-branded button — a black pill with a "Deposit stablecoins / In a Blink" label and USDC/USDT coin marks. It is self-contained (inline styles, data-URI icons, no CSS imports) and fills the width of its container.

```tsx theme={null}
import { BlinkDepositButton, useBlinkDeposit } from '@swype-org/deposit/react';

function Checkout({ userWalletAddress }: { userWalletAddress: string }) {
  const { status, error, displayMessage, requestDeposit } = useBlinkDeposit({
    signer: '/api/sign-payment',
  });

  return (
    <>
      <BlinkDepositButton
        onClick={() =>
          requestDeposit({
            amount: 50,
            chainId: 8453,
            address: userWalletAddress,
            token: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
          })
        }
        loading={status === 'signer-loading'}
      />
      {error && <p className="error">{displayMessage}</p>}
    </>
  );
}
```

The button is presentation-only — wire `onClick` to `requestDeposit` yourself, and pass `disabled` or `loading` to reflect your app's state. Its branding is intentionally fixed; if you need a custom look, keep building your own button as shown above. See the [BlinkDepositButton reference](/sdk-reference/blink-deposit-button) for the full props table.

## Configuration

```typescript theme={null}
const deposit = new Deposit({
  // Required: URL string or custom async function
  signer: '/api/sign-payment',

  // Base URL of the hosted payment webview app.
  // Default: 'https://pay.blink.cash'
  webviewBaseUrl: 'https://pay.blink.cash',

  // Origin for postMessage validation. Derived from webviewBaseUrl when omitted.
  hostedFlowOrigin: 'https://pay.blink.cash',

  // DOM element to mount the iframe overlay into. Default: document.body
  containerElement: document.getElementById('transfer-root')!,

  // Max ms to wait for signer response. Default: 15000
  signerTimeoutMs: 15_000,

  // Max ms for entire flow (signer + user completion). No limit by default.
  flowTimeoutMs: 300_000,

  // Allow the hosted flow to show the full widget (deposit address options
  // alongside Blink one tap). Set to false to send users straight to the
  // Blink one-tap flow. Default: true
  enableFullWidget: true,

  // Enable debug logging. Default: false
  debug: false,
});
```

### Full widget vs. one tap

By default the hosted flow can open with the **full widget** — a deposit options entry screen that offers deposit addresses (pay from an exchange or any wallet) alongside Blink's one-tap deposit. Set `enableFullWidget: false` to skip it and send every user straight to the Blink one-tap flow.

The full widget only appears when **both** your SDK config and your Blink merchant account have it enabled. If it is disabled on your merchant account, the hosted flow stays in one-tap mode regardless of this option — contact Blink to change your account configuration.

## Custom signer function

If you need full control over the HTTP call to your signer (custom headers, auth tokens, different HTTP method), pass a function instead of a URL:

```typescript theme={null}
import type { SignerFunction } from '@swype-org/deposit';

const deposit = new Deposit({
  signer: async (data) => {
    const res = await fetch('/api/sign-payment', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${getSessionToken()}`,
      },
      body: JSON.stringify(data),
    });
    if (!res.ok) throw new Error(`Signer error: ${res.status}`);
    return res.json();
  },
});
```

## Deposit request fields

| Field            | Type                     | Required | Description                                                                                                                     |
| ---------------- | ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `amount`         | `number`                 | Yes      | USD amount to deposit (> 0).                                                                                                    |
| `chainId`        | `number`                 | Yes      | Destination chain ID. Dynamic per user. For example, `8453` for Base or `792703809` for Solana.                                 |
| `address`        | `string`                 | Yes      | Destination wallet address. For the web SDK this can be an EVM address or a Solana address, depending on the destination chain. |
| `token`          | `string`                 | Yes      | Destination token identifier. Use an EVM token contract address on EVM chains or an SPL mint address on Solana.                 |
| `callbackScheme` | `string \| null`         | No       | Always `null` for browser integrations. Reserved for native app deep links.                                                     |
| `reference`      | `string`                 | No       | Merchant order/invoice ID for reconciliation.                                                                                   |
| `metadata`       | `Record<string, string>` | No       | Arbitrary key-value pairs forwarded to your signer.                                                                             |

<Info>
  The destination `chainId`, `address`, and `token` are **not** static merchant configuration. They are set dynamically per transaction, typically based on the user's embedded wallet.
</Info>

<Info>
  Choose destination values from Blink's active routing catalog. See [Supported Networks and Wallets](/integration/supported-networks-and-wallets) for the current wallet, chain, token, and fee model details.
</Info>

## Deposit result

When the payment completes, the SDK returns a `DepositResult`:

```typescript theme={null}
interface DepositResult {
  transfer: TransferSummary;
  idempotencyKey?: string;
  preview?: {
    amount: number;
    chainId: number;
    address: string;
    token: string;
  };
}

interface TransferSummary {
  id: string;
  status: string;
  amount?: { amount: number; currency: string };
  destinations?: Array<{
    chainId: string;
    address: string;
    token?: { address?: string; symbol?: string };
  }>;
}
```

## Status flow

| Status           | Meaning                                                           |
| ---------------- | ----------------------------------------------------------------- |
| `idle`           | No active flow.                                                   |
| `signer-loading` | Calling the merchant signer endpoint.                             |
| `iframe-active`  | Hosted flow iframe is open, waiting for user to complete payment. |
| `completed`      | Transfer succeeded.                                               |
| `error`          | Something failed.                                                 |

```typescript theme={null}
deposit.on('status-change', (status) => console.log('Status:', status));

deposit.status;    // current status
deposit.result;    // last DepositResult (when completed)
deposit.error;     // last DepositError (when error)
deposit.isActive;  // true during signer-loading or iframe-active
```

## Error handling

See [Error Codes](/sdk-reference/errors) for the full reference. Every error is a `DepositError` with a machine-readable `code`:

```typescript theme={null}
import { DepositError, getDisplayMessage } from '@swype-org/deposit';

try {
  await deposit.requestDeposit({ /* ... */ });
} catch (err) {
  if (err instanceof DepositError) {
    showToast(getDisplayMessage(err));
    console.error(err.code, err.message);
  }
}
```

## Lifecycle

```typescript theme={null}
// Close the deposit iframe without waiting for completion
deposit.close();

// Tear down and release all resources (call on unmount / page unload)
deposit.destroy();
```

## Metadata and order reference

Pass merchant-specific data through the flow for reconciliation:

```typescript theme={null}
await deposit.requestDeposit({
  amount: 50,
  chainId: 8453,
  address: '0x...',
  token: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
  reference: 'order-123',
  metadata: { invoiceId: 'INV-456', customerId: 'cust-789' },
});
```

The `reference` and `metadata` are forwarded to your signer endpoint so you can correlate the payment with your internal records.

## Security

The deposit UI loads live from `https://pay.blink.cash`. Before going live, lock
the iframe origin with a Content-Security-Policy on your page and learn how to
verify the build:

```http theme={null} theme={null}
Content-Security-Policy: frame-src https://pay.blink.cash; script-src 'self';
```

See [Security & Content Integrity](/integration/security) for the full model,
including passkey delegation and build-manifest verification.

## Mobile browser considerations

* **Full-screen iframe:** On viewports 480px or narrower, the transfer iframe automatically renders full-screen with no border radius.
* **Passkey / WebAuthn:** The SDK transparently handles WebAuthn ceremonies between the cross-origin iframe and the parent page. No additional configuration is needed.
* **`callbackScheme`:** Always pass `null` (or omit) for browser integrations. Reserved for native app deep-link flows.
* **Viewport meta tag:** Ensure your page includes a proper viewport meta tag:

```html theme={null}
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
```
