> ## 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.

# Descripción general de Open Loop Cards

> Genera enlaces alojados de tarjetas virtuales ("open-loop" Send Cards) desde tu plataforma. Llamas a una API para acuñar uno o más enlaces y luego entregas esos enlaces a los destinatarios como prefieras: por correo electrónico, por SMS o devolviendo las URL sin procesar para incrustarlas en tus propios flujos. Cuando un destinatario abre el enlace, llega a una página alojada por Fluz, se verifica y reclama una tarjeta virtual de una sola carga, financiada desde tu cuenta.

<Info>
  **Requisitos previos:** un token Bearer de acceso con el alcance `CREATE_SHARE_LINK`. La autenticación básica se rechaza. Contacta a tu representante de ventas para habilitar el acceso. Consulta [Autenticación](/concepts/authentication).
</Info>

<Note>
  **Qué significan "hosted" / "open-loop".** Un enlace *hosted* apunta a una página de activación alojada por Fluz. *Open-loop* significa que la tarjeta virtual resultante es una tarjeta de red (estilo Visa/Mastercard) que se puede gastar en muchos comercios, sujeta a las reglas de tu programa — no una tarjeta de regalo de marca única de circuito cerrado.
</Note>

export const ensureCardStyles = () => {
  if (typeof document === 'undefined') return;
  if (document.getElementById('fjs-om-card-css')) return;
  const st = document.createElement('style');
  st.id = 'fjs-om-card-css';
  st.textContent = `
@keyframes omGlisten{0%{transform:translateX(0) skewX(-18deg)}26%{transform:translateX(900%) skewX(-18deg)}100%{transform:translateX(900%) skewX(-18deg)}}
.om-wrap{--omh:0;container-type:inline-size;display:block;margin:1.6rem 0;text-decoration:none!important;border-bottom:none!important;background-image:none!important;box-shadow:none!important}
.om-wrap:hover,.om-wrap:focus,.om-wrap:active{text-decoration:none!important;border-bottom:none!important;background-image:none!important;box-shadow:none!important;text-decoration-thickness:0!important}
.om-wrap::before,.om-wrap::after{content:none!important;display:none!important}
.om-wrap:hover,.om-wrap:focus-visible{--omh:1}
.om-wrap:focus-visible{outline:2px solid #FEC251;outline-offset:3px}
.om-card{position:relative;display:block;border:1px solid #EAEAEA;border-radius:20px;overflow:hidden;background:#F5F4F3;cursor:pointer;aspect-ratio:684/448;padding:36px 0 0;transition:box-shadow .45s cubic-bezier(.2,.8,.2,1)}
.om-glow{position:absolute;inset:0;pointer-events:none;background:radial-gradient(72cqw 72cqw at -14% 22%,rgba(142,112,72,.18),transparent 78%),radial-gradient(72cqw 72cqw at 114% 22%,rgba(142,112,72,.18),transparent 78%)}
.om-grid{position:absolute;inset:0;pointer-events:none;opacity:.13;background-image:linear-gradient(rgba(26,0,0,.5) 1px,transparent 1px),linear-gradient(90deg,rgba(26,0,0,.5) 1px,transparent 1px);background-size:3.51cqw 3.51cqw;background-position:0 3.29cqw,3.29cqw 0;mask-image:radial-gradient(34cqw 34cqw at -14% 16%,#000 32%,rgba(0,0,0,.45) 72%,transparent 100%),radial-gradient(34cqw 34cqw at 114% 16%,#000 32%,rgba(0,0,0,.45) 72%,transparent 100%);-webkit-mask-image:radial-gradient(34cqw 34cqw at -14% 16%,#000 32%,rgba(0,0,0,.45) 72%,transparent 100%),radial-gradient(34cqw 34cqw at 114% 16%,#000 32%,rgba(0,0,0,.45) 72%,transparent 100%)}
.om-text{position:relative;display:block;text-align:left;padding:0 30% 0 5.4%}
.om-title{display:block;font-family:'Fjs Greed','Greed',ui-sans-serif,sans-serif;font-weight:600;font-size:34px;line-height:1.12;letter-spacing:-.02em;color:#1A0000}
.om-sub{display:block;font-family:'Fjs Area','Area',ui-sans-serif,sans-serif;font-size:16px;line-height:1.35;font-weight:600;color:#6E6862;margin-top:9px}
.om-frame{position:relative;display:block;margin:7.2cqw auto 0;width:85.7cqw;border:1.05cqw solid #0A0A0A;border-bottom:0;border-radius:1.9cqw 1.9cqw 0 0;background:#0A0A0A;box-shadow:0 2.4cqw 5cqw -1.6cqw rgba(26,0,0,.34);transform:translateY(calc(var(--omh,0) * -9px));transition:transform .55s cubic-bezier(.2,.8,.2,1)}
.om-shot{display:block;border-radius:.95cqw .95cqw 0 0;overflow:hidden;background:#fff;aspect-ratio:3444/2081}
.om-shot img{display:block;width:100%;height:100%;object-fit:cover;object-position:top center;margin:0}
.om-img-dark{display:none}
.om--dark .om-img-light{display:none}
.om--dark .om-img-dark{display:block}
html.dark .om--system .om-img-light{display:none}
html.dark .om--system .om-img-dark{display:block}
.om-fade{position:absolute;left:0;right:0;bottom:0;height:13cqw;pointer-events:none;background:linear-gradient(to bottom,rgba(245,244,243,0) 0%,rgba(245,244,243,.7) 46%,#F5F4F3 100%)}
.om-dim{position:absolute;inset:0;pointer-events:none;background:linear-gradient(to bottom,rgba(20,20,22,.1) 0%,rgba(20,20,22,.22) 60%,rgba(20,20,22,.3) 100%);opacity:var(--omh,0);transition:opacity .45s ease}
.om-pill{position:absolute;right:3.4%;top:9px;pointer-events:none;display:inline-flex;align-items:center;justify-content:center;gap:9px;height:48px;padding:0 26px;border-radius:100px;overflow:hidden;background:#FEC251;color:#1A0000;font-family:'Fjs Greed','Greed',ui-sans-serif,sans-serif;font-weight:600;font-size:19px;letter-spacing:-.01em;transform:scale(calc(1 + .05 * var(--omh,0)));transition:transform .5s cubic-bezier(.2,.8,.2,1),box-shadow .5s ease;box-shadow:0px 0px 2.4px 0.4px #FEC25159,0 0 calc(22px * var(--omh,0)) calc(6px * var(--omh,0)) rgba(254,194,81,calc(.5 * var(--omh,0)))}
.om-pill svg{width:18px;height:18px;flex:none}
.om-glisten{position:absolute;top:0;left:-22%;width:16%;height:100%;background:linear-gradient(100deg,rgba(255,255,255,0) 0%,rgba(255,255,255,.42) 50%,rgba(255,255,255,0) 100%);filter:blur(2px);animation:omGlisten 3.6s cubic-bezier(.45,0,.55,1) infinite}
@container (max-width:659px){
  .om-card{aspect-ratio:684/588;padding-top:26px}
  .om-title{font-size:27px}
  .om-sub{font-size:13px;margin-top:6px}
  .om-text{padding:0 7% 0 5.4%}
  .om-pill{position:static;margin-top:22px;height:40px;padding:0 20px;gap:7px;font-size:16px}
  .om-pill svg{width:15px;height:15px}
  .om-frame{margin-top:32px}
}
.om--dark .om-card,html.dark .om--system .om-card{background:#221919;border-color:#2E2823}
.om--dark .om-glow,html.dark .om--system .om-glow{background:radial-gradient(72cqw 72cqw at -14% 22%,rgba(235,222,196,.24),transparent 80%),radial-gradient(72cqw 72cqw at 114% 22%,rgba(235,222,196,.24),transparent 80%)}
.om--dark .om-grid,html.dark .om--system .om-grid{opacity:.09;background-image:linear-gradient(rgba(229,223,195,.85) 1px,transparent 1px),linear-gradient(90deg,rgba(229,223,195,.85) 1px,transparent 1px)}
.om--dark .om-title,html.dark .om--system .om-title{color:#EAEAEA}
.om--dark .om-sub,html.dark .om--system .om-sub{color:#9C9391}
.om--dark .om-frame,html.dark .om--system .om-frame{box-shadow:0 2.4cqw 5cqw -1.6cqw rgba(0,0,0,.5)}
.om--dark .om-shot,html.dark .om--system .om-shot{background:#17110C}
.om--dark .om-fade,html.dark .om--system .om-fade{background:linear-gradient(to bottom,rgba(34,25,25,0) 0%,rgba(34,25,25,.7) 46%,#221919 100%)}
.om--dark .om-dim,html.dark .om--system .om-dim{background:linear-gradient(to bottom,rgba(8,5,4,.18) 0%,rgba(8,5,4,.34) 60%,rgba(8,5,4,.44) 100%)}
`;
  document.head.appendChild(st);
};

export const ensureLoader = () => new Promise((resolve, reject) => {
  if (window.FluzDemo) return resolve();
  let s = document.querySelector('script[data-fluz-loader]');
  if (!s) {
    s = document.createElement('script');
    s.src = 'https://demos.fluz.app/loader-v1.js';
    s.async = true;
    s.setAttribute('data-fluz-loader', '1');
    document.head.appendChild(s);
  }
  s.addEventListener('load', () => resolve());
  s.addEventListener('error', () => reject(new Error('loader failed')));
});

export const DemoCard = ({demo, url, theme = 'light', image, imageDark, title = 'Demo open loop cards', subtitle = 'Step-by-step walkthrough of the API and user experience.', cta = 'Launch demo', presentation = 'full', mode}) => {
  ensureCardStyles();
  if (typeof window !== 'undefined') {
    ensureLoader().catch(() => {});
    try {
      const origin = new URL(url, 'https://demos.fluz.app').origin;
      if (!document.querySelector(`link[rel="preconnect"][href="${origin}"]`)) {
        const pc = document.createElement('link');
        pc.rel = 'preconnect';
        pc.href = origin;
        pc.crossOrigin = 'anonymous';
        document.head.appendChild(pc);
      }
    } catch (e) {}
  }
  const imgLight = image || '/images/demos/demo-shot.png';
  const imgDark = imageDark || '/images/demos/demo-shot-dark.png';
  return <a className={`om-wrap${theme === 'dark' ? ' om--dark' : theme === 'system' ? ' om--system' : ''}`} href={url || '#'} target="_blank" rel="noopener" aria-label={`Launch the interactive ${title} demo`} data-fluz-demo-open={demo} data-fluz-demo-url={url} data-fluz-demo-src="docs" data-fluz-demo-presentation={presentation} data-fluz-demo-mode={mode} onClick={e => {
    if (url && window.innerWidth < 600) return;
    e.preventDefault();
    const opener = e.currentTarget;
    ensureLoader().then(() => window.FluzDemo.open(demo, {
      url,
      src: 'docs',
      presentation,
      mode,
      opener
    })).catch(() => {
      if (url) window.open(url, '_blank', 'noopener');
    });
  }}>
      <span className="om-card">
        <span className="om-glow" /><span className="om-grid" />
        <span className="om-text">
          <span className="om-title">{title}</span>
          <span className="om-sub">{subtitle}</span>
          <span className="om-pill">
            {cta}
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.6" strokeLinecap="round" aria-hidden="true">
              <path d="M14 4h6v6M20 4l-8 8M10 20H4v-6M4 20l8-8" />
            </svg>
            <span className="om-glisten" />
          </span>
        </span>
        <span className="om-frame"><span className="om-shot">
          <img className="om-img-light" src={imgLight} alt={`${title} — interactive demo`} />
          <img className="om-img-dark" src={imgDark} alt="" aria-hidden="true" />
        </span></span>
        <span className="om-fade" /><span className="om-dim" />
      </span>
    </a>;
};

<DemoCard demo="card-issuing" url="https://demos.fluz.app/card-issuing/" theme="system" />

## Cómo funciona

<Steps>
  <Step title="Tú generas enlaces">
    Llama a `generateVCShareLinks` con la oferta, el límite de la tarjeta, la cantidad, la fuente de fondos y un método de entrega. Cada enlace representa una tarjeta con su propio límite, financiada desde la cuenta de gasto que especifiques.
  </Step>

  <Step title="Fluz crea una solicitud de compartición por enlace">
    Cada enlace se asigna a una solicitud de compartición (`PENDING`) y a una URL alojada.
  </Step>

  <Step title="El enlace se entrega">
    Con `GENERATE_URL` recibes las URL para distribuirlas tú mismo. Con `EMAIL` o `PHONE_NUMBER`, Fluz entrega un enlace a cada destinatario por ti.
  </Step>

  <Step title="El destinatario activa y reclama la tarjeta">
    El destinatario abre el enlace y verifica su número de teléfono con un código de un solo uso — sin descargar app ni contraseña. El límite de la tarjeta se descuenta de tu cuenta de gasto en el momento de la reclamación, no cuando se genera el enlace. La tarjeta no se muestra automáticamente al reclamarla; al revelarla se le pide al destinatario que introduzca su PIN o que cree uno si aún no lo ha configurado. Consulta [Experiencia del destinatario](/features/open-loop-cards/open-loop-cards-recipient-experience) para el recorrido completo.
  </Step>
</Steps>

El destinatario se convierte en usuario autorizado solo de ese objeto de tarjeta virtual — no obtiene acceso a tu cuenta, saldos ni a ninguna otra tarjeta.

![Diagrama del flujo de envío de tarjetas](https://files.readme.io/c19029bfeaca196f7eadc2f020ab56f2aad893261f561307bec543e700f0d74b-diagram.svg)

## Disponibilidad y alcance

| Capacidad                                 | Estado                                       |
| ----------------------------------------- | -------------------------------------------- |
| Tarjetas virtuales de una sola carga      | ✅ Compatible                                 |
| Tarjetas virtuales de un solo uso         | ✅ Compatible                                 |
| Tarjetas recargables                      | ❌ No compatible                              |
| Generar enlaces vía API                   | ✅ Compatible                                 |
| Generar enlaces vía Importación CSV       | ❌ Próximamente                               |
| Enlaces alojados de **tarjeta de regalo** | ❌ Fuera de alcance (solo tarjetas virtuales) |

El tipo de objeto de enlace de compartición de tarjeta es `VIRTUAL_CARD` y el tipo de tarjeta es `SINGLE_LOAD`.

<Warning>
  **Tarjetas de regalo:** A pesar del encuadre "tarjetas virtuales **y** tarjetas de regalo" de la iniciativa más amplia, hoy no existe un flujo alojado para reclamar tarjetas de regalo. Los saldos de tarjetas de regalo aparecen en esta área solo como una *posible fuente de fondos* para tarjetas virtuales alojadas (planificado, aún no habilitado). Documenta y construye únicamente para tarjetas virtuales.
</Warning>

## Referencia de operaciones

Hay tres operaciones públicas, todas controladas por el alcance `CREATE_SHARE_LINK`:

| Operación                | Tipo     | Propósito                                              |
| ------------------------ | -------- | ------------------------------------------------------ |
| `generateVCShareLinks`   | Mutation | Crear uno o más enlaces alojados de tarjetas virtuales |
| `getVCShareLinks`        | Query    | Listar/inspeccionar enlaces generados previamente      |
| `deactivateVCShareLinks` | Mutation | Desactivar (expirar) enlaces que generaste             |

Todas las operaciones de Send Cards están en la API GraphQL de Fluz en `POST https://<your-fluz-api-host>/api/v1/graphql` con un encabezado `Authorization: Bearer <access_token>`. El token debe incluir el alcance `CREATE_SHARE_LINK` — sin él, toda operación devuelve *"Missing permissions! Please contact your sales rep to get access to generate VC share links."*

## generateVCShareLinks

Crea `quantity` solicitudes de compartición y devuelve un enlace alojado por cada solicitud.

```graphql theme={null}
mutation GenerateVCShareLinks($input: GenerateVCShareLinksInput!) {
  generateVCShareLinks(input: $input) {
    shareLinks
  }
}
```

### Campos de entrada

| Campo                    | Tipo                                    | Requerido   | Descripción                                                                                                                                                                                                                                                           |
| ------------------------ | --------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cardLimit`              | `Int!`                                  | Sí          | Límite de gasto (y monto de carga) para cada tarjeta, en unidades monetarias enteras. Debe ser un número entero ≥ al mínimo del programa.                                                                                                                             |
| `offerId`                | `String!`                               | Sí          | UUID v4 de la oferta del comercio a la que está vinculada la tarjeta. La oferta debe estar activa y su comercio debe ser compartible.                                                                                                                                 |
| `quantity`               | `Int!`                                  | Sí          | Número de enlaces a generar. Se crea una URL alojada distinta por unidad.                                                                                                                                                                                             |
| `shareMethod`            | `ShareMethodType!`                      | Sí          | Cómo se identifican los destinatarios y cómo se entregan los enlaces: `GENERATE_URL`, `EMAIL`, `PHONE_NUMBER`, `EXISTING_USER` o `REGISTER_USER`.                                                                                                                     |
| `userCashBalanceId`      | `UUID`                                  | Sí\*        | La cuenta de gasto utilizada para financiar las tarjetas. *Marcado como opcional en el esquema, pero requerido en la práctica — omitirlo falla la validación.*                                                                                                        |
| `daysUntilExpiration`    | `Int`                                   | No          | Días hasta que el enlace expira. Mínimo 1. Si se omite, usa el valor predeterminado del programa (30 días). **Esta fecha también se convierte en la fecha de bloqueo/congelación de la tarjeta** — consulta [Vencimiento y congelación](#expiration--freeze).         |
| `recipientListEmail`     | `[String]`                              | Condicional | Requerido y no vacío cuando `shareMethod = EMAIL`. Su longitud debe ser igual a `quantity`. Debe estar vacío en caso contrario.                                                                                                                                       |
| `recipientListPhone`     | `[String]`                              | Condicional | Requerido y no vacío cuando `shareMethod = PHONE_NUMBER`. Su longitud debe ser igual a `quantity`. Debe estar vacío en caso contrario.                                                                                                                                |
| `recipientUserIds`       | `[UUID]`                                | Condicional | IDs de usuario de Fluz conocidos para vincular como destinatarios cuando `shareMethod = EXISTING_USER`. Su longitud debe ser igual a `quantity`. Mutuamente excluyente con `recipientRegistrations`.                                                                  |
| `recipientRegistrations` | `[ShareLinkRecipientRegistrationInput]` | Condicional | Cargas útiles de pre-registro en línea cuando `shareMethod = REGISTER_USER` — Fluz crea o reutiliza un usuario provisional (sin asiento) por entrada antes de generar enlaces. Su longitud debe ser igual a `quantity`. Mutuamente excluyente con `recipientUserIds`. |
| `usePrepaymentBalance`   | `Boolean`                               | No          | Configura si se usará tu saldo de Prepago como fuente de fondos adicional. El valor predeterminado es false. Más información sobre cómo se usa: [Cómo funciona la financiación](/features/virtual-cards#how-funding-works).                                           |
| `useRewardsBalance`      | `Boolean`                               | No          | Configura si se usará tu Fluz Rewards Balance como fuente de fondos adicional. El valor predeterminado es false. Más información sobre cómo se usa: [Cómo funciona la financiación](/features/virtual-cards#how-funding-works).                                       |

<Note>
  **Fuente de fondos.** `userCashBalanceId` (una cuenta de gasto perteneciente a tu cuenta emisora) es la fuente de fondos principal y requerida. Opcionalmente configura `usePrepaymentBalance` y/o `useRewardsBalance` en `true` para permitir que Fluz recurra a tu saldo de prepago o recompensas si la cuenta de gasto no cubre el monto total en el momento de la reclamación. Las cuentas bancarias y tarjetas bancarias no son compatibles como fuentes de fondos.
</Note>

### Identificación del destinatario y entrega (`shareMethod`)

| Valor           | Comportamiento                                                                                                                                                                            | Campo del destinatario                                                       | Tarjeta creada                                     |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------- |
| `GENERATE_URL`  | Fluz devuelve las URL alojadas en la respuesta. Tú mismo las distribuyes.                                                                                                                 | Ambas listas deben estar vacías/omitidas.                                    | Al reclamar                                        |
| `EMAIL`         | Fluz envía por correo un enlace a cada destinatario.                                                                                                                                      | `recipientListEmail` requerido; la longitud debe ser igual a `quantity`.     | Al reclamar                                        |
| `PHONE_NUMBER`  | Fluz envía por SMS un enlace a cada destinatario.                                                                                                                                         | `recipientListPhone` requerido; la longitud debe ser igual a `quantity`.     | Al reclamar                                        |
| `EXISTING_USER` | El enlace se vincula a un usuario conocido de Fluz desde el inicio; solo ese usuario puede reclamarlo. Se entrega mediante el(los) canal(es) de contacto ya registrados para ese usuario. | `recipientUserIds` requerido; la longitud debe ser igual a `quantity`.       | **Inmediatamente**, en el momento de la generación |
| `REGISTER_USER` | Fluz crea o reutiliza un usuario provisional por entrada y luego vincula el enlace a ese usuario, igual que `EXISTING_USER`.                                                              | `recipientRegistrations` requerido; la longitud debe ser igual a `quantity`. | **Inmediatamente**, en el momento de la generación |

<Note>
  `EXISTING_USER` y `REGISTER_USER` crean la tarjeta virtual como parte de la llamada a `generateVCShareLinks`, en lugar de aplazar la creación de la tarjeta al momento de la reclamación. Consulta [Registrar y Enviar](/features/open-loop-cards/register-and-send) para el flujo completo de `EXISTING_USER`, incluido cómo registrar primero a un destinatario con `registerUser`.
</Note>

### Reglas de validación

* `cardLimit` debe ser un número entero y al menos el mínimo del programa.
* `offerId` debe ser un UUID v4 válido para una oferta **activa** cuyo **comercio sea compartible**.
* `quantity` debe ser un número entero.
* El campo de destinatario que coincide con `shareMethod` (`recipientListEmail`, `recipientListPhone`, `recipientUserIds` o `recipientRegistrations`) debe tener una longitud **igual a** `quantity`. Las discrepancias devuelven un error claro y **no** crean registros.
* `recipientUserIds` y `recipientRegistrations` son mutuamente excluyentes entre sí y con los campos de listas de entrega.
* Cada ID en `recipientUserIds` debe ser un usuario de Fluz válido y existente.
* `userCashBalanceId` es requerido y debe ser un UUID v4 válido perteneciente a la cuenta del emisor. `usePrepaymentBalance` y `useRewardsBalance` son fuentes de respaldo opcionales y pueden habilitarse junto con él.
* Tipos de tarjeta inválidos o entradas mal formadas devuelven errores claros y no crean registros.

### Ejemplos

<CodeGroup>
  ```json Generate URLs theme={null}
    {
      "input": {
        "cardLimit": 25,
        "offerId": "11111111-2222-3333-4444-555555555555",
        "daysUntilExpiration": 30,
        "quantity": 3,
        "shareMethod": "GENERATE_URL",
        "userCashBalanceId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
      }
    }
  ```

  ```json Email theme={null}
    {
      "input": {
        "cardLimit": 25,
        "offerId": "11111111-2222-3333-4444-555555555555",
        "daysUntilExpiration": 30,
        "quantity": 2,
        "shareMethod": "EMAIL",
        "recipientListEmail": ["recipient1@example.com", "recipient2@example.com"],
        "userCashBalanceId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
      }
    }
  ```

  ```json SMS theme={null}
    {
      "input": {
        "cardLimit": 25,
        "offerId": "11111111-2222-3333-4444-555555555555",
        "daysUntilExpiration": 30,
        "quantity": 2,
        "shareMethod": "PHONE_NUMBER",
        "recipientListPhone": ["+12125550101", "+12125550102"],
        "userCashBalanceId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
      }
    }
  ```

  ```json Existing user theme={null}
    {
      "input": {
        "cardLimit": 25,
        "offerId": "11111111-2222-3333-4444-555555555555",
        "daysUntilExpiration": 30,
        "quantity": 1,
        "shareMethod": "EXISTING_USER",
        "recipientUserIds": ["f1320ac4-52dc-4c67-9e80-24e506b18450"],
        "userCashBalanceId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
      }
    }
  ```
</CodeGroup>

<Tip>
  Para `EXISTING_USER`, registra primero al destinatario (o usa directamente el ID de un usuario existente) — consulta [Registrar y Enviar](/features/open-loop-cards/register-and-send) para el recorrido completo, incluida la llamada `registerUser` y el manejo de la respuesta.
</Tip>

### Respuesta

```json theme={null}
{
  "data": {
    "generateVCShareLinks": {
      "shareLinks": [
        "https://fluz.app/virtual-prepaid-card/3f8a...c1",
        "https://fluz.app/virtual-prepaid-card/9b2d...77",
        "https://fluz.app/virtual-prepaid-card/0c41...e3"
      ]
    }
  }
}
```

`shareLinks` es un arreglo de URL alojadas, una por `quantity`, cada una con la forma `https://fluz.app/virtual-prepaid-card/{share_request_id}`.

<Tip>
  La respuesta devuelve solo las URL. Para recuperar el **ID del lote** y los **IDs visibles** de los enlaces que acabas de crear (necesarios para listar y desactivar), usa `getVCShareLinks` filtrado por estado.
</Tip>

## getVCShareLinks

Lista los enlaces de compartición generados previamente para que puedas inspeccionar el estado, los destinatarios, el vencimiento y la tarjeta emitida.

```graphql theme={null}
query GetVCShareLinks($input: GetVCShareLinksInput!) {
  getVCShareLinks(input: $input) {
    senderAppId
    shareRequestBatchId
    shareRequestDisplayId
    shareObjectStatus
    recipientPhone
    recipientEmail
    linkExpirationDate
    virtualCardId
    linkUrl
    shareRequestDetails {
      cardLimit
      offerId
      daysUntilExpiration
      quantity
      shareMethod
      recipientListEmail
      recipientListPhone
      userCashBalanceId
    }
  }
}
```

### Campos de entrada

| Campo                    | Tipo                  | Descripción                                                 |
| ------------------------ | --------------------- | ----------------------------------------------------------- |
| `shareObjectStatuses`    | `[ShareObjectStatus]` | Filtrar por estado: `PENDING`, `ISSUED`, `USED`, `EXPIRED`. |
| `shareRequestBatchIds`   | `[String]`            | Devolver solo enlaces en estos lotes.                       |
| `shareRequestDisplayIds` | `[String]`            | Devolver solo los enlaces con estos IDs visibles.           |

<Tip>
  **Flujo recomendado.** En la primera llamada, filtra solo por `shareObjectStatuses`. La respuesta te da `shareRequestBatchId` y `shareRequestDisplayId`; úsalos para filtrar con precisión en llamadas posteriores (y para desactivar).
</Tip>

### Campos de respuesta (`GeneratedShareLink`)

| Campo                   | Tipo                  | Descripción                                                                             |
| ----------------------- | --------------------- | --------------------------------------------------------------------------------------- |
| `senderAppId`           | `String`              | La app/aplicación de desarrollador que generó el enlace.                                |
| `shareRequestBatchId`   | `String`              | Identificador de lote compartido por todos los enlaces generados en una misma llamada.  |
| `shareRequestDisplayId` | `String`              | Identificador por enlace, legible para humanos.                                         |
| `shareObjectStatus`     | `ShareObjectStatus`   | `PENDING`, `ISSUED`, `USED` o `EXPIRED`.                                                |
| `recipientEmail`        | `String`              | Correo del destinatario, si se entregó por email.                                       |
| `recipientPhone`        | `String`              | Teléfono del destinatario, si se entregó por SMS.                                       |
| `linkExpirationDate`    | `DateTime`            | Cuándo expira el enlace / se congela la tarjeta.                                        |
| `virtualCardId`         | `String`              | El ID de la tarjeta virtual emitida, una vez reclamada.                                 |
| `linkUrl`               | `String`              | La URL alojada del enlace.                                                              |
| `shareRequestDetails`   | `ShareRequestDetails` | La configuración original (límite de tarjeta, oferta, cantidad, entrega, financiación). |

### Ejemplos

<CodeGroup>
  ```json By status theme={null}
    { "input": { "shareObjectStatuses": ["PENDING", "ISSUED"] } }
  ```

  ```json By batch theme={null}
    { "input": { "shareRequestBatchIds": ["ABC123", "XYZ789"] } }
  ```

  ```json By display ID theme={null}
    { "input": { "shareRequestDisplayIds": ["SR-000001", "SR-000002"] } }
  ```
</CodeGroup>

## deactivateVCShareLinks

Desactiva (expira) enlaces que generaste — por ejemplo, si un lote se envió por error o necesitas revocar enlaces no reclamados. Desactivar un enlace lo establece en `EXPIRED`; un enlace no reclamado ya no se puede reclamar.

```graphql theme={null}
mutation DeactivateVCShareLinks($input: DeactivateVCShareLinksInput!) {
  deactivateVCShareLinks(input: $input)
}
```

### Campos de entrada

| Campo                    | Tipo       | Descripción                                         |
| ------------------------ | ---------- | --------------------------------------------------- |
| `shareRequestBatchIds`   | `[String]` | Desactivar cada enlace en estos lotes.              |
| `shareRequestDisplayIds` | `[String]` | Desactivar solo los enlaces con estos IDs visibles. |

Obtén los IDs de lote desde `getVCShareLinks`.

```json theme={null}
{ "input": { "shareRequestBatchIds": ["ABC123"] } }
```

Devuelve una cadena de confirmación legible, p. ej., `"3 share requests successfully deactivated!"`.

<Warning>
  Si un destinatario ya ha **reclamado** un enlace (estado `ISSUED`/`USED`), desactivar el enlace no recupera la tarjeta emitida. Para detener el gasto en una tarjeta ya emitida, usa los controles relevantes del ciclo de vida/congelación de la tarjeta.
</Warning>

## Vencimiento y congelación

La fecha de vencimiento del enlace cumple doble función:

* **Vencimiento del enlace** — después de esta fecha, un enlace **no reclamado** ya no se puede reclamar.
* **Fecha de bloqueo/congelación de la tarjeta** — para una tarjeta **emitida**, esta es la fecha de bloqueo (fin de ese día). Después de ella, la tarjeta se congela y no se puede gastar.
* **La expiración de la tarjeta** se alinea con el fin del mes de la fecha de congelación (p. ej., una fecha de congelación del 15/6/2026 produce una expiración de tarjeta del 30/6/2026).

Configura la ventana con `daysUntilExpiration` en el momento de la generación. Si se omite, se usa el valor predeterminado del programa (30 días). Esta fecha se muestra al destinatario (normalmente como una fecha de "Válida hasta") — consulta [Experiencia del destinatario](/features/open-loop-cards/open-loop-cards-recipient-experience).

## Referencia de estados y errores

### Estados del objeto de compartición

| Estado    | Significado                                                       |
| --------- | ----------------------------------------------------------------- |
| `PENDING` | Enlace generado, aún no reclamado.                                |
| `ISSUED`  | El destinatario reclamó el enlace; se emitió una tarjeta virtual. |
| `USED`    | La tarjeta emitida ha sido utilizada.                             |
| `EXPIRED` | El enlace venció o fue desactivado; ya no se puede reclamar.      |

Para los estados que ve un destinatario cuando un enlace está vencido, revocado o ya reclamado, consulta [Errores de enlace visibles para el destinatario](/features/open-loop-cards/open-loop-cards-recipient-experience#recipient-facing-link-errors).

### Errores comunes de la API

| Causa                                                                                                                                       | Resultado                                         |
| ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
| Token Bearer faltante/inválido o alcance `CREATE_SHARE_LINK` faltante                                                                       | Solicitud rechazada (no autorizada).              |
| Longitud del campo de destinatario (`recipientListEmail`, `recipientListPhone`, `recipientUserIds` o `recipientRegistrations`) ≠ `quantity` | Error de validación claro; no se crean registros. |
| `recipientUserIds` hace referencia a un usuario de Fluz inexistente                                                                         | Error de validación; no se crean registros.       |
| Oferta inactiva, comercio no compartible o `offerId` inválido                                                                               | Error de validación; no se crean registros.       |
| `userCashBalanceId` faltante/inválido                                                                                                       | Error de validación; no se crean registros.       |

## Notas y limitaciones

* **La URL devuelta es el destino alojado, no un enlace corto.** Internamente, los enlaces también se envuelven con un proveedor de enlaces cortos, pero la API devuelve la URL alojada canónica (`/virtual-prepaid-card/{share_request_id}`). Distribuye la URL exactamente como se devuelve.
* `userCashBalanceId` es efectivamente requerido aunque el esquema lo marque como opcional.
* **Campos ocultos/internos no forman parte de esta API.** El tipo de objeto y el tipo de tarjeta son fijos (`VIRTUAL_CARD` / `SINGLE_LOAD`). La financiación con cuenta bancaria y tarjeta bancaria aún no está habilitada; no las envíes. `usePrepaymentBalance` y `useRewardsBalance` son las únicas fuentes de fondos adicionales compatibles hoy.
* **No se admiten enlaces alojados de tarjetas de regalo.** Esta API es solo para tarjetas virtuales.

## Próximos pasos

<CardGroup cols={2}>
  <Card title="Experiencia del destinatario" icon="user" href="/features/open-loop-cards/open-loop-cards-recipient-experience">
    Lo que ven tus destinatarios cuando abren un enlace alojado y las reglas que rigen su tarjeta.
  </Card>

  <Card title="Registrar y Enviar" icon="id-card" href="/features/open-loop-cards/register-and-send">
    Usa `EXISTING_USER` para registrar a un destinatario y crear su tarjeta por adelantado, en lugar de al reclamar.
  </Card>

  <Card title="Crear una orden masiva" icon="layers" href="/features/create-bulk-order">
    Emite muchas tarjetas a la vez para su distribución programática.
  </Card>
</CardGroup>
