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

# Vincular cuenta bancaria vía Plaid

Usa estas operaciones para permitir que un usuario conecte una cuenta bancaria a Fluz mediante **Plaid Link**. La vinculación es el punto de entrada para toda la financiación desde cuentas bancarias: una vez conectada una cuenta, puedes leer su saldo y capacidad de gasto, extraer su historial de transacciones y reparar la conexión si luego se cae, cada uno cubierto en su propia página.

Transactional Graph Service (TGS) expone un envoltorio público GraphQL sobre la integración de Plaid de Fluz. Identity-service sigue siendo el sistema de registro para tokens de Plaid, ingestión de cuentas bancarias, saldos, transacciones históricas y webhooks de Plaid. TGS nunca devuelve tokens de acceso de Plaid ni tokens públicos a tu app.

## Auth

Llama a `/api/v1/graphql` con un **token Bearer de usuario** de Fluz que incluya el alcance `MANAGE_PAYMENT`.

Los campos de Plaid **no** permiten autenticación Basic. Obtén primero un token de acceso de usuario mediante el flujo estándar de auth de TGS y luego llama a estos campos con:

```http theme={null}
Authorization: Bearer <fluz-user-access-token>
```

## La vista rápida del flujo de vinculación

1. Crea un token de Link (`createPlaidLinkToken`).
2. Abre Plaid Link con ese token.
3. En el `onSuccess` de Plaid, envía el `public_token` devuelto a TGS (`completePlaidLink`).
4. Guarda el `platformItemId` de la respuesta de finalización: es el identificador público seguro que usarás más adelante para volver a vincular.

### 1. Crear un token de Link

```graphql theme={null}
mutation CreatePlaidLinkToken($input: CreatePlaidLinkTokenInput!) {
  createPlaidLinkToken(input: $input) {
    linkToken
    expiration
    requestId
    mode
  }
}
```

```json theme={null}
{
  "input": {}
}
```

Para Link **nativo**, pasa `deviceOs` como `IOS` o `ANDROID` para que identity-service incluya la opción correcta de redirección OAuth de Plaid.

```json theme={null}
{
  "input": {
    "deviceOs": "IOS"
  }
}
```

### 2. Abrir Plaid Link

Inicializa Plaid Link con el `linkToken` devuelto. (Consulta el ejemplo del Web SDK a continuación).

### 3. Completar la vinculación

En el `onSuccess` de Plaid Link, envía el `public_token` devuelto a TGS.

```graphql theme={null}
mutation CompletePlaidLink($input: CompletePlaidLinkInput!) {
  completePlaidLink(input: $input) {
    requiresAddress
    bankAccountId
    bankInstitutionAuthId
    newlyLinkedBankInstitutionAuthId
    bankInstitutionName
    platformItemId
    bankAccounts {
      bankInstitutionAuthId
      bankAccountId
      bankName
      lastFour
      type
      subtype
    }
  }
}
```

```json theme={null}
{
  "input": {
    "publicToken": "public-sandbox-..."
  }
}
```

### 4. Guardar el `platformItemId`

Guarda `platformItemId` de la respuesta de finalización. Este es el identificador público seguro que se usa más tarde para volver a vincular una conexión desconectada. Si no se devuelve, llama a `getPlaidBankAccounts` para el usuario y almacena el `platformItemId` persistido de esa respuesta.

Si `completePlaidLink` devuelve `requiresAddress: true`, adjunta una dirección antes de que la cuenta sea utilizable — consulta **Adjuntar una dirección** más abajo.

## Ejemplo del Plaid Web SDK

El script del Web SDK de Plaid debe cargarse directamente desde el CDN de Plaid.

```html theme={null}
<script src="https://cdn.plaid.com/link/v2/stable/link-initialize.js"></script>
```

Crea un token de Link a través de TGS, inicializa Plaid Link con ese token y pasa el `public_token` de Plaid de vuelta a TGS en `onSuccess`. Llamar a `startPlaidLink()` sin argumento inicia una **nueva** vinculación; llamar a `startPlaidLink(existingPlatformItemId)` inicia una **revinculación** (consulta *Relinking Disconnected Bank Accounts*).

```javascript theme={null}
const FLUZ_GRAPHQL_URL = '<fluz-api-base-url>/api/v1/graphql';
const fluzUserAccessToken = '<fluz-user-access-token>';

const fluzGraphql = async (query, variables) => {
  const response = await fetch(FLUZ_GRAPHQL_URL, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${fluzUserAccessToken}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ query, variables }),
  });

  const body = await response.json();
  if (!response.ok || body.errors?.length) {
    throw new Error(body.errors?.[0]?.message ?? 'Fluz GraphQL request failed');
  }

  return body.data;
};

const createPlaidLinkToken = async (platformItemId) => {
  const data = await fluzGraphql(
    `
      mutation CreatePlaidLinkToken($input: CreatePlaidLinkTokenInput!) {
        createPlaidLinkToken(input: $input) {
          linkToken
          mode
        }
      }
    `,
    { input: { platformItemId } },
  );

  return data.createPlaidLinkToken;
};

const completePlaidLink = async (publicToken, platformItemId) => {
  const data = await fluzGraphql(
    `
      mutation CompletePlaidLink($input: CompletePlaidLinkInput!) {
        completePlaidLink(input: $input) {
          requiresAddress
          bankInstitutionAuthId
          newlyLinkedBankInstitutionAuthId
          bankAccountId
          platformItemId
          bankAccounts {
            bankInstitutionAuthId
            bankAccountId
            bankName
            lastFour
            type
            subtype
          }
        }
      }
    `,
    { input: { publicToken, platformItemId } },
  );

  return data.completePlaidLink;
};

const getPlaidBankAccounts = async (input) => {
  const data = await fluzGraphql(
    `
      query GetPlaidBankAccounts($input: PlaidBankAccountFilterInput) {
        getPlaidBankAccounts(input: $input) {
          platformItemId
          bankInstitutionAuthId
          bankAccountId
          bankInstitutionName
          lastFour
          type
          subtype
        }
      }
    `,
    { input },
  );

  return data.getPlaidBankAccounts;
};

const resolvePlatformItemId = async (completion) => {
  if (completion.platformItemId) return completion.platformItemId;

  const bankInstitutionAuthId =
    completion.bankInstitutionAuthId ?? completion.newlyLinkedBankInstitutionAuthId;
  const accounts = await getPlaidBankAccounts({ bankInstitutionAuthId });
  return accounts[0]?.platformItemId;
};

const startPlaidLink = async (platformItemId) => {
  const { linkToken } = await createPlaidLinkToken(platformItemId);

  const handler = window.Plaid.create({
    token: linkToken,
    onSuccess: async (publicToken, metadata) => {
      const result = await completePlaidLink(publicToken, platformItemId);
      const persistedPlatformItemId = await resolvePlatformItemId(result);

      await saveConnectionForRelinkLater({
        platformItemId: persistedPlatformItemId,
        bankInstitutionAuthId:
          result.bankInstitutionAuthId ?? result.newlyLinkedBankInstitutionAuthId,
        bankAccounts: result.bankAccounts,
        plaidLinkSessionId: metadata.link_session_id,
      });
    },
    onExit: (error, metadata) => {
      console.log('Plaid Link exited', { error, metadata });
    },
  });

  handler.open();
};
```

## Listar cuentas vinculadas

Devuelve las cuentas bancarias seguras de Plaid para el usuario autenticado.

```graphql theme={null}
query GetPlaidBankAccounts($input: PlaidBankAccountFilterInput) {
  getPlaidBankAccounts(input: $input) {
    platformItemId
    bankInstitutionAuthId
    bankInstitutionName
    bankAccountId
    accountName
    lastFour
    type
    subtype
    status
  }
}
```

```json theme={null}
{
  "input": {
    "platformItemId": "plaid-item-id"
  }
}
```

## Adjuntar una dirección

Si `completePlaidLink` devolvió `requiresAddress: true`, adjunta una dirección a la institución bancaria vinculada.

```graphql theme={null}
mutation CreatePlaidLinkAddress($input: CreatePlaidLinkAddressInput!) {
  createPlaidLinkAddress(input: $input) {
    addressId
  }
}
```

```json theme={null}
{
  "input": {
    "bankInstitutionAuthId": "bank-institution-auth-id",
    "address": {
      "streetAddressLine1": "123 Main St",
      "streetAddressLine2": "Apt 4",
      "city": "New York",
      "state": "NY",
      "postalCode": "10001",
      "country": "US"
    }
  }
}
```

## Quitar una conexión bancaria

Quita una institución bancaria de Plaid de la cuenta del usuario.

```graphql theme={null}
mutation RemovePlaidBankInstitution($input: RemovePlaidBankInstitutionInput!) {
  removePlaidBankInstitution(input: $input) {
    removed
    bankInstitutionAuthId
  }
}
```

```json theme={null}
{
  "input": {
    "bankInstitutionAuthId": "bank-institution-auth-id",
    "reason": "USER_REQUESTED"
  }
}
```

## Límites de seguridad

TGS nunca devuelve tokens de acceso de Plaid, tokens públicos de Plaid, detalles de coincidencia de identidad, números de cuenta bancaria ni números de ruta. Los nombres y alias de cuentas bancarias pueden ser etiquetas bancarias ingresadas por el usuario y se devuelven solo como metadatos de la cuenta.

Identity-service sigue siendo responsable de:

* Intercambio de tokens de Plaid
* Almacenamiento del token de acceso de Plaid
* Creación o reparación de instituciones y cuentas bancarias
* Obtención de saldos
* Ingesta de transacciones históricas
* Eliminación de instituciones bancarias
* Webhooks de Plaid

TGS no expone identity-service directamente y no agrega una ruta pública de webhook de Plaid.

## Flujos no compatibles

La vinculación manual de microdepósitos de Plaid no es compatible intencionalmente con este envoltorio público. Las nuevas vinculaciones y revinculaciones deben usar el flujo normal de Plaid Link, que admite saldos y transacciones históricas.

Si un usuario inicia una nueva vinculación para la misma institución en lugar de elegir revincular, complétala como una vinculación nueva normal. Identity-service se encarga de la desduplicación y reparación de cuentas bancarias; guarda el `platformItemId` devuelto después de la finalización.

## Manejo de errores

Si `completePlaidLink` se agota o falla después de que Plaid devolvió un `public_token`, primero llama a `getPlaidBankAccounts` usando el `platformItemId` almacenado o esperado antes de reintentar. Reintenta solo si Plaid devuelve un `public_token` nuevo desde una sesión de Link nueva — los tokens públicos de Plaid son de corta duración y de un solo uso.

<br />
