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

# Connections

> Permanent standing orders that auto-convert between crypto and fiat

## What is a Connection?

A connection (BringinLink) is a **permanent standing order** that links a crypto address to a bank account. Once active, conversions happen automatically — any amount, any time, no further API calls needed.

Think of it as a bridge: one side is crypto, the other is fiat. Funds sent to either side are automatically converted and delivered to the other.

### Supported Connection Types

| Type             | Direction     | Subtypes        | What happens                                              |
| ---------------- | ------------- | --------------- | --------------------------------------------------------- |
| `FIAT_TO_CRYPTO` | EUR to crypto | `ONCHAIN`       | User sends EUR via SEPA, receives crypto at their address |
| `CRYPTO_TO_FIAT` | Crypto to EUR | `ONCHAIN`, `LN` | User sends crypto, receives EUR via SEPA to their bank    |

### Supported Currencies & Networks

<Tabs>
  <Tab title="Buy (EUR to Crypto)">
    | Currency | Networks                           | Default      |
    | -------- | ---------------------------------- | ------------ |
    | `BTC`    | `BTC`                              | `BTC`        |
    | `ETH`    | `ETH`                              | `ETH`        |
    | `USDC`   | `USDC` (Ethereum), `POLYGON`       | Must specify |
    | `USDT`   | `USDT` (Polygon), `ETH` (Ethereum) | Must specify |
    | `POL`    | `POL`, `POLYGON`                   | `POLYGON`    |
  </Tab>

  <Tab title="Sell (Crypto to EUR)">
    | Currency | Subtypes        |
    | -------- | --------------- |
    | `BTC`    | `LN`, `ONCHAIN` |
    | `ETH`    | `ONCHAIN`       |
    | `USDC`   | `ONCHAIN`       |
  </Tab>
</Tabs>

<Warning>Invalid currency/network combinations return a `400 INVALID_CURRENCY_NETWORK_COMBINATION` error. For example, BTC on POLYGON or ETH on BTC are not valid.</Warning>

***

## Buy Connection (EUR to Crypto)

<Steps>
  <Step title="You provide a crypto address">
    Your app passes the user's destination address when creating the connection.
  </Step>

  <Step title="Bringin returns a deposit IBAN">
    Once the connection is active, you receive a unique SEPA IBAN.
  </Step>

  <Step title="User sends EUR from their bank">
    Any amount, any time — regular bank transfer to the deposit IBAN.
  </Step>

  <Step title="Bringin converts and delivers crypto">
    EUR is received, converted at market rate, and sent to the linked address.
  </Step>
</Steps>

```mermaid theme={null}
flowchart LR
    A[User's Bank] -->|EUR transfer| B[Deposit IBAN]
    B --> C[Bringin converts]
    C -->|Crypto| D[User's Address]
```

<Info>Your app stores and displays the deposit IBAN. The user sends EUR whenever they want to buy crypto — it's as simple as a bank transfer.</Info>

## Sell Connection (Crypto to EUR)

### On-chain

<Steps>
  <Step title="You provide an IBAN + bank details">
    Your app passes the user's bank account details when creating the connection.
  </Step>

  <Step title="Bringin returns a crypto deposit address">
    Once the connection is active, you receive a unique deposit address.
  </Step>

  <Step title="User sends crypto to the deposit address">
    Any amount, any time — standard on-chain transaction.
  </Step>

  <Step title="Bringin converts and delivers EUR">
    Crypto is received, converted at market rate, and sent to the linked bank account via SEPA.
  </Step>
</Steps>

```mermaid theme={null}
flowchart LR
    A[User's Wallet] -->|Crypto| B[Deposit Address]
    B --> C[Bringin converts]
    C -->|EUR via SEPA| D[User's Bank Account]
```

### Lightning Network

For BTC sell connections, you can use Lightning for instant, low-fee transactions:

<Steps>
  <Step title="You provide an IBAN + Lightning address username">
    Your app passes the bank details and a unique Lightning username (e.g., `alice_wise`).
  </Step>

  <Step title="Bringin creates a Lightning address">
    The username becomes `{username}@bringin.xyz` — a permanent receive address.
  </Step>

  <Step title="User sends BTC via Lightning">
    Any Lightning wallet can send to the address. Instant settlement.
  </Step>

  <Step title="Bringin converts and delivers EUR">
    BTC is converted at market rate and sent to the linked bank account via SEPA.
  </Step>
</Steps>

<Warning>
  **Use deterministic addresses for on-chain connections.** Connections are permanent — use a fixed derivation path (e.g., `m/84'/0'/0'/0/0`) for Bitcoin addresses. Avoid `getNewReceiveAddress()` which generates a different address each time.
</Warning>

***

## How to Create a Connection

### Option 1: Single API call (recommended)

Bundle everything into `POST /application/connect`. Bringin handles onboarding, connection creation, and SMS confirmation. You receive webhooks when it's done.

```bash theme={null}
POST /api/v0/application/connect
```

<Tabs>
  <Tab title="Buy (EUR to BTC)">
    ```json Request theme={null}
    {
      "email": "user@example.com",
      "callback": "https://yourapp.com/bringin-webhooks",
      "ref": "your-internal-user-id",
      "direction": "FIAT_TO_CRYPTO",
      "btcAddress": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
      "currency": "BTC",
      "network": "BTC"
    }
    ```
  </Tab>

  <Tab title="Sell (BTC to EUR)">
    ```json Request theme={null}
    {
      "email": "user@example.com",
      "callback": "https://yourapp.com/bringin-webhooks",
      "ref": "your-internal-user-id",
      "direction": "CRYPTO_TO_FIAT",
      "iban": "DE89370400440532013000",
      "bic": "COBADEFFXXX",
      "beneficiaryName": "Alice Smith",
      "currency": "BTC",
      "network": "BTC"
    }
    ```
  </Tab>

  <Tab title="Sell via Lightning">
    ```json Request theme={null}
    {
      "email": "user@example.com",
      "callback": "https://yourapp.com/bringin-webhooks",
      "ref": "your-internal-user-id",
      "direction": "CRYPTO_TO_FIAT",
      "iban": "DE89370400440532013000",
      "bic": "COBADEFFXXX",
      "beneficiaryName": "Alice Smith",
      "lnAddress": "alice_wise",
      "currency": "BTC",
      "network": "BTC"
    }
    ```
  </Tab>
</Tabs>

Bringin handles the rest. You'll receive:

1. `/verification-status` webhook with the per-user api-key
2. `/connection-status` webhook with the deposit IBAN (or deposit address / LN address)

See [Onboarding API](/api-reference/onboarding/connect-user) and [Webhooks](/webhooks) for details.

### Option 2: Direct BringinLink API (advanced)

Use this if you already have the user's per-user api-key and want granular control over the connection lifecycle.

<Steps>
  <Step title="List existing connections">
    **Always call `GET /bringin-link` first** before creating a new connection. This prevents duplicate errors and lets you reuse pending connections.

    ```bash theme={null}
    GET /api/v0/bringin-link
    ```

    Check the response:

    * **ACTIVE link for same destination** — Use it directly, no creation needed
    * **PENDING link** — Show OTP screen with the existing `challengeId`
    * **INITIATING link** — Poll for status update
    * **No match** — Safe to create a new connection
  </Step>

  <Step title="Create the connection">
    ```bash theme={null}
    POST /api/v0/bringin-link
    ```

    <Tabs>
      <Tab title="Buy (EUR to BTC)">
        ```json Request theme={null}
        {
          "name": "Blue Wallet",
          "type": "FIAT_TO_CRYPTO",
          "subtype": "ONCHAIN",
          "destinationAddress": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
          "destinationCurrency": "BTC",
          "network": "BTC"
        }
        ```
      </Tab>

      <Tab title="Sell (BTC to EUR)">
        ```json Request theme={null}
        {
          "name": "Wise account",
          "type": "CRYPTO_TO_FIAT",
          "subtype": "ONCHAIN",
          "sourceCurrency": "BTC",
          "beneficiary": {
            "iban": "DE89370400440532013000",
            "bic": "COBADEFFXXX",
            "name": "Alice Smith"
          }
        }
        ```
      </Tab>

      <Tab title="Sell via Lightning">
        ```json Request theme={null}
        {
          "name": "Wise LN",
          "type": "CRYPTO_TO_FIAT",
          "subtype": "LN",
          "lnAddress": "alice_wise",
          "beneficiary": {
            "iban": "DE89370400440532013000",
            "bic": "COBADEFFXXX",
            "name": "Alice Smith"
          }
        }
        ```
      </Tab>
    </Tabs>

    Returns **202 Accepted** with `status: "INITIATING"`. Creation is asynchronous — you must poll for updates.
  </Step>

  <Step title="Poll for status">
    Poll `GET /bringin-link/{id}` every 2-3 seconds until the status changes from `INITIATING`.

    | Status       | Action                                                                   |
    | ------------ | ------------------------------------------------------------------------ |
    | `INITIATING` | Keep polling (processing takes 5-15 seconds)                             |
    | `PENDING`    | Show OTP screen — use `challengeId` from the response                    |
    | `ACTIVE`     | Connection is ready (standing order was reused, no OTP needed)           |
    | `FAILED`     | Show a user-friendly error (see [handling failures](#handling-failures)) |

    <Warning>If still `INITIATING` after 60 seconds, show a timeout message and suggest contacting [support@bringin.xyz](mailto:support@bringin.xyz).</Warning>
  </Step>

  <Step title="Confirm with SMS OTP">
    When the status is `PENDING`, the user receives an SMS OTP. Confirm it:

    ```bash theme={null}
    POST /api/v0/bringin-link/confirm
    ```

    ```json Request theme={null}
    {
      "challengeId": "abc123",
      "verificationCode": "123456"
    }
    ```

    On success, the connection becomes `ACTIVE` and auto-conversions begin.

    If the user needs a new code, call `POST /bringin-link/resend-otp` with the existing `challengeId`.

    <Note>The OTP expires 30 minutes after creation. The resend endpoint does NOT reset this timer. If expired, create a new connection.</Note>
  </Step>
</Steps>

See [Connections API Reference](/api-reference/connections/create) for full endpoint docs.

***

## Status Lifecycle

```mermaid theme={null}
stateDiagram-v2
    [*] --> INITIATING: POST /bringin-link
    INITIATING --> PENDING: Processing complete
    INITIATING --> FAILED: Error
    PENDING --> ACTIVE: OTP confirmed
    PENDING --> EXPIRED: 30 min timeout
    ACTIVE --> INACTIVE: User cancels
```

| Status       | Terminal? | Description                                           |
| ------------ | --------- | ----------------------------------------------------- |
| `INITIATING` | No        | Record created, async processing in progress          |
| `PENDING`    | No        | Standing order created, awaiting SMS OTP confirmation |
| `ACTIVE`     | No        | OTP confirmed, auto-conversions enabled               |
| `FAILED`     | Yes       | Creation failed (support can resume)                  |
| `EXPIRED`    | Yes       | OTP not confirmed within 30 minutes                   |
| `INACTIVE`   | Yes       | Cancelled by user                                     |

***

## Best Practices

<AccordionGroup>
  <Accordion title="Always check for existing connections first" icon="magnifying-glass">
    Before calling `POST /bringin-link`, always call `GET /bringin-link` to list the user's existing connections. This prevents:

    * `409 DUPLICATE_BRINGIN_LINK` — A link already exists for this destination
    * `409 DUPLICATE_DESTINATION_ADDRESS` — Address already has an active connection
    * `409 DUPLICATE_IBAN` — IBAN already has an active connection
    * Unnecessary resource creation on each failed attempt
  </Accordion>

  <Accordion title="Reuse PENDING connections instead of creating duplicates" icon="recycle">
    If a connection exists in `PENDING` status:

    1. Check if `standingOrder.challengeExpiresAt` is still in the future
    2. If valid — show the OTP screen with the existing `challengeId`
    3. If the user needs a new code — call `POST /bringin-link/resend-otp`
    4. If expired — wait for auto-expiry, then create a new connection
  </Accordion>

  <Accordion title="Connection names must be unique" icon="tag">
    The `name` field must be unique across all non-terminal connections (`INITIATING`, `PENDING`, `ACTIVE`) for the user. Names from `FAILED`, `EXPIRED`, and `INACTIVE` connections are freed up for reuse.
  </Accordion>

  <Accordion title="Handle Lightning address usernames carefully" icon="bolt">
    Lightning address usernames (`lnAddress` field):

    * 3-30 characters, lowercase alphanumeric plus `_` and `-`
    * Must start and end with an alphanumeric character
    * No consecutive `_` or `-`
    * Globally unique — if taken, the API returns `409 LN_ADDRESS_EXISTS`
    * The resulting address is `{username}@bringin.xyz`
  </Accordion>
</AccordionGroup>

***

## Handling Failures

When a connection creation fails (`status: FAILED`):

<Warning>
  **Never show raw error messages to end users.** The `error` field contains technical details meant for debugging. Instead, show:

  *"Something went wrong while setting up your connection. Please contact [support@bringin.xyz](mailto:support@bringin.xyz) for help."*
</Warning>

Failed connections are resumable by the Bringin support team. Your app does not need to handle resume logic — direct users to support.

Provide a **"Try Again"** button that starts a fresh creation flow (list existing connections first, then create).

***

## Cancelling a Connection

To cancel an active connection, use the standing order cancellation flow:

<Steps>
  <Step title="Initiate cancellation">
    ```bash theme={null}
    POST /api/v0/bringin-link/{standingOrderId}/cancel
    ```

    The response depends on whether the standing order is shared with other connections:

    **Soft cancel** (Lightning connections sharing a standing order):

    ```json Response theme={null}
    { "immediate": true }
    ```

    Only your app's connection is deactivated. No OTP needed. Done.

    **Hard cancel** (last/only connection for this standing order):

    ```json Response theme={null}
    {
      "challengeId": "cancel_challenge_123",
      "dateExpires": "2026-03-24T09:05:00.000Z"
    }
    ```

    An SMS OTP is sent to the user. Proceed to step 2.
  </Step>

  <Step title="Confirm cancellation with OTP">
    Only needed when the initiate response contains `challengeId`:

    ```bash theme={null}
    POST /api/v0/bringin-link/{standingOrderId}/cancel/confirm
    ```

    ```json Request theme={null}
    {
      "challengeId": "cancel_challenge_123",
      "verificationCode": "654321"
    }
    ```

    On success, the standing order is cancelled and all associated connections become `INACTIVE`.
  </Step>
</Steps>

***

## Error Reference

| HTTP | Error Code                             | When                                           | What to do                                   |
| ---- | -------------------------------------- | ---------------------------------------------- | -------------------------------------------- |
| 400  | `INVALID_BTC_ADDRESS`                  | BTC address fails format check                 | Show "Please enter a valid Bitcoin address"  |
| 400  | `INVALID_CURRENCY_NETWORK_COMBINATION` | Unsupported currency + network pair            | Show valid networks for the currency         |
| 400  | `RECENTLY_FAILED_IBAN`                 | Same IBAN failed within last 10 minutes        | Show "Please wait before retrying this IBAN" |
| 409  | `DUPLICATE_NAME`                       | Name already used by active/pending connection | Prompt for a different name                  |
| 409  | `DUPLICATE_IBAN`                       | Active connection already exists for this IBAN | Use the existing connection                  |
| 409  | `DUPLICATE_BRINGIN_LINK`               | Connection already exists for this destination | Use the existing connection                  |
| 409  | `DUPLICATE_DESTINATION_ADDRESS`        | Active connection for this address + currency  | Use the existing connection                  |
| 409  | `LN_ADDRESS_EXISTS`                    | Lightning address taken                        | Choose a different username                  |
| 409  | `CONCURRENT_REQUEST`                   | Another creation in progress                   | Wait 2-3 seconds and retry                   |
| 429  | `TOO_MANY_PENDING_BRINGIN_LINKS`       | 10+ pending connections                        | Confirm or wait for existing to expire       |

<Tip>Most `409` errors can be avoided by listing existing connections before creating a new one.</Tip>
