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

# Tu primera compra de tarjeta de regalo

> Ejecuta el flujo feliz completo de punta a punta: deposita fondos, explora comercios, compra una tarjeta de regalo y revélala, todo en el sandbox.

Este Quickstart te lleva desde una cuenta nueva de Fluz hasta una compra completada en staging. Registrarás una app, emitirás un token de acceso con alcance específico, luego depositarás fondos, explorarás el catálogo de comercios, comprarás una tarjeta de regalo y revelarás sus detalles de canje, todo en el sandbox, donde no se mueve dinero real.

<Info>
  **Requisitos previos**

  * Una **cuenta de Fluz**: crearás credenciales de API de staging y emitirás un token de acceso en los Pasos 1–2 a continuación.
  * Las solicitudes van al endpoint GraphQL del **sandbox**. Nada aquí carga una tarjeta real: consulta [Entorno de Staging vs. Live](/concepts/environments).
  * Cada mutación necesita un `idempotencyKey` único (un UUID generado por el cliente) para que una solicitud solo se procese una vez: consulta [Idempotencia](/concepts/idempotency).
</Info>

## Antes de comenzar

Excepto para emitir tu token (Paso 2), cada llamada es una solicitud `POST` a un único endpoint GraphQL, autenticada con tu token bearer:

<CodeGroup>
  ```bash Endpoint & headers theme={null}
  POST https://transactional-graph.staging.fluzapp.com/api/v1/graphql

  Authorization: Bearer <YOUR_USER_ACCESS_TOKEN>
  Content-Type: application/json
  ```

  ```bash Example request (cURL) theme={null}
  curl -X POST https://transactional-graph.staging.fluzapp.com/api/v1/graphql \
    -H "Authorization: Bearer <YOUR_USER_ACCESS_TOKEN>" \
    -H "Content-Type: application/json" \
    -d '{
      "query": "query getWallet { getWallet { bankCards { bankCardId lastFourDigits } } }"
    }'
  ```
</CodeGroup>

<Tip>
  Tu cuenta de sandbox incluye una **tarjeta bancaria de prueba preagregada**, para que puedas ejecutar todo este flujo de inmediato. Agrega más tarjetas de prueba o cuentas bancarias desde la [página de Sandbox Accounts](https://uni.staging.fluzapp.com/accounts-and-cards) usando valores de la lista [Test Bank Cards](/test-bank-cards).
</Tip>

## El flujo completo

<Steps>
  <Step title="Regístrate y registra una app" icon="user-plus">
    Crea una cuenta de Fluz en [fluz.app](https://fluz.app), luego abre la [Consola de Desarrolladores](https://uni.staging.fluzapp.com/developers) y crea una nueva aplicación **Staging**. Cuando aparezcan las credenciales, copia tu `clientId` y `clientSecret`: el secreto solo se muestra una vez.

    Guía completa: [Prepara tus cuentas](/get-started/prepare-accounts).
  </Step>

  <Step title="Intercambia credenciales por un token de acceso" icon="key">
    Emite un token de acceso de usuario de corta duración y con alcance específico a partir de las credenciales de tu app. Esta llamada va al servicio OAuth; todas las llamadas posteriores usan el token devuelto como credencial Bearer.

    <CodeGroup>
      ```bash Mint token (cURL) theme={null}
      curl -X POST https://oauth-service.fluzapp.com/graphql \
        -H "Content-Type: application/json" \
        -d '{
          "query": "mutation ($clientId: String!, $clientSecret: String!, $scopes: [Scope!]!) { generateUserAccessToken(clientId: $clientId, clientSecret: $clientSecret, scopes: $scopes) { accessToken expiresIn scopes } }",
          "variables": {
            "clientId": "<APPLICATION_CLIENT_ID>",
            "clientSecret": "<APPLICATION_CLIENT_SECRET>",
            "scopes": ["LIST_OFFERS", "LIST_PAYMENT", "MANAGE_PAYMENT", "PURCHASE_GIFTCARD", "REVEAL_GIFTCARD"]
          }
        }'
      ```

      ```json Response theme={null}
      {
        "data": {
          "generateUserAccessToken": {
            "accessToken": "eyJhbGciOi...",
            "expiresIn": 3600,
            "scopes": ["LIST_OFFERS", "LIST_PAYMENT", "MANAGE_PAYMENT", "PURCHASE_GIFTCARD", "REVEAL_GIFTCARD"]
          }
        }
      }
      ```
    </CodeGroup>

    Almacena el `accessToken` devuelto y envíalo como `Authorization: Bearer <accessToken>` en cada solicitud a continuación. Los tokens son de corta duración (`expiresIn` está en segundos): emítelos del lado del servidor y renuévalos antes del vencimiento. Detalles completos: [Credenciales de API](/get-started/api-credentials).

    <Tip>
      Los scopes controlan lo que el token puede hacer: incluye solo lo que tu flujo necesita: `MANAGE_PAYMENT` para agregar fuentes de fondos y depositar, `LIST_OFFERS` para explorar el catálogo, y `PURCHASE_GIFTCARD` / `REVEAL_GIFTCARD` para comprar y revelar.
    </Tip>

    <Warning>
      Nunca expongas el `clientSecret` en un navegador o cliente móvil. Emite tokens del lado del servidor y reenvía solo el token.
    </Warning>
  </Step>

  <Step title="Deposita fondos a tu saldo de Fluz" icon="wallet">
    Precargar un saldo de Fluz generalmente hace que las compras de tarjetas de regalo sean más rápidas y puede evitar ciertos controles de velocidad. Puedes depositar de dos maneras:

    * **Manualmente**, a través del [sitio web de Fluz en sandbox](https://uni.staging.fluzapp.com/manage-money).
    * **Programáticamente**, a través de la mutación `depositCashBalance` (se muestra a continuación).

    <Accordion title="Primero, recupera un ID de método de pago (getWallet)" icon="id-card">
      Para depositar vía API necesitas el ID de una fuente de fondos (por ejemplo, un `bankCardId`). Ejecuta `getWallet` y guarda el ID que quieres usar.

      ```bash theme={null}
      curl -X POST https://transactional-graph.staging.fluzapp.com/api/v1/graphql \
        -H "Authorization: Bearer <YOUR_USER_ACCESS_TOKEN>" \
        -H "Content-Type: application/json" \
        -d '{
          "query": "query getWallet { getWallet { bankCards { bankCardId lastFourDigits } bankAccounts { bankAccountId } } }"
        }'
      ```

      Toma el `bankCardId` o `bankAccountId` de la respuesta. Administrar fuentes de fondos vía API requiere el scope `MANAGE_PAYMENT`. Más detalle: [Ver fuentes de fondos](/features/view-funding-sources).
    </Accordion>

    ### Realiza el depósito

    Usa la mutación `depositCashBalance`. Acepta un objeto `DepositCashBalanceInput`.

    <CodeGroup>
      ```graphql Mutation theme={null}
      mutation depositCashBalance($input: DepositCashBalanceInput!) {
        depositCashBalance(input: $input) {
          cashBalanceDeposits {
            cashBalanceDepositId
            depositDisplayId
            depositAmount
            status
            cashBalanceDepositType
          }
          balances {
            cashBalance { availableBalance totalBalance pendingBalance }
            giftCardCashBalance { availableBalance totalBalance pendingBalance }
          }
        }
      }
      ```

      ```json Variables theme={null}
      {
        "input": {
          "idempotencyKey": "0284be6f-1a69-44f7-9da0-5b5edaf45d19",
          "amount": 100.00,
          "depositType": "CASH_BALANCE",
          "bankCardId": "0284be6f-1a69-44f7-9da0-5b5edaf45d19"
        }
      }
      ```
    </CodeGroup>

    #### Campos de entrada

    <ParamField body="idempotencyKey" type="string" required>
      Un UUID único generado por el cliente que garantiza que el depósito se procese solo una vez.
    </ParamField>

    <ParamField body="amount" type="Float" required>
      El monto a depositar.
    </ParamField>

    <ParamField body="depositType" type="CashBalanceDepositType">
      Saldo de destino. Uno de `CASH_BALANCE`, `GIFT_CARD_BALANCE` o `RESERVE_BALANCE`.
    </ParamField>

    <ParamField body="Funding source" type="UUID">
      De dónde proviene el dinero: proporciona **uno** de `bankAccountId`, `bankCardId` o `paypalVaultId`.
    </ParamField>

    <ParamField body="merchantCategoryCode" type="Int">
      Solo para `GIFT_CARD_BALANCE`. Un MCC de cuatro dígitos que clasifica el negocio. Usa `getMccList` para recuperar valores válidos.
    </ParamField>

    <ParamField body="userCashBalanceId" type="UUID">
      Cuando se selecciona `CASH_BALANCE`, la cuenta de gasto específica en la que depositar.
    </ParamField>

    <Accordion title="Respuesta de ejemplo" icon="code">
      ```json theme={null}
      {
        "data": {
          "depositCashBalance": {
            "cashBalanceDeposits": [
              {
                "cashBalanceDepositId": "5f5c5b5f-4c4b-4e4e-9e9e-4f4f4f4f4f4f",
                "depositDisplayId": "102370",
                "depositAmount": "100.00",
                "status": "COMPLETED",
                "cashBalanceDepositType": "CASH_BALANCE"
              }
            ],
            "balances": {
              "cashBalance": {
                "availableBalance": "100.00",
                "totalBalance": "100.00",
                "pendingBalance": "0.00"
              },
              "giftCardCashBalance": {
                "availableBalance": "0.00",
                "totalBalance": "0.00",
                "pendingBalance": "0.00"
              }
            }
          }
        }
      }
      ```
    </Accordion>

    <Note>
      Los depósitos pueden liquidarse al instante o dentro de 2–5 días hábiles según la fuente de fondos y el tipo de liquidación. El objeto `balances` en la respuesta refleja tu saldo disponible actual. Consulta [Verificar saldo de cuenta](/check-account-balance) para volver a consultarlo en cualquier momento.
    </Note>
  </Step>

  <Step title="Explora comercios y elige una oferta" icon="store">
    Con un saldo listo, obtén el catálogo de comercios disponibles y sus ofertas de cashback usando `getMerchants`.

    <CodeGroup>
      ```graphql Query theme={null}
      query GetMerchantCatalog(
        $paginate: OffsetInput
        $offerTypes: OfferTypesInput
        $filterBy: FilterByInput
      ) {
        getMerchants(paginate: $paginate, offerTypes: $offerTypes, filterBy: $filterBy) {
          merchantId
          name
          slug
          logoUrl
          faceplateUrl
          offers {
            offeringMerchantId
            offerId
            type
            deliveryFormat
            barcodeType
            offerRates {
              maxUserRewardValue
              cashbackVoucherRewardValue
              boostRewardValue
              denominations
              allowedPaymentMethods
            }
          }
        }
      }
      ```

      ```json Variables theme={null}
      {
        "paginate": { "limit": 20, "offset": 0 },
        "offerTypes": { "giftCardOffer": true, "cardLinkedOffer": false }
      }
      ```
    </CodeGroup>

    <Tip>
      El catálogo se ordena por porcentaje de cashback de forma predeterminada, por lo que las ofertas más altas aparecen primero.
    </Tip>

    #### Argumentos útiles

    <ParamField query="name" type="String">
      Filtra comercios por nombre.
    </ParamField>

    <ParamField query="paginate" type="OffsetInput">
      `{ limit, offset }`. El `limit` predeterminado y máximo es `20`.
    </ParamField>

    <ParamField query="offerTypes" type="OfferTypesInput">
      Indicadores booleanos para qué tipos de ofertas devolver, por ejemplo `{ giftCardOffer: true, cardLinkedOffer: false }`.
    </ParamField>

    <ParamField query="filterBy" type="FilterByInput">
      Filtra ofertas dentro de cada comercio, por ejemplo por `deliveryFormat` (`URL`, `CODES`, `PIN_AS_CODE`, `PIN_WITH_URL`).
    </ParamField>

    <Note>
      **Paginación:** la respuesta puede devolver menos resultados que tu `limit`. Para extraer el **catálogo completo**, sigue incrementando `offset` por tu `limit` y detente cuando la API devuelva un arreglo vacío (`[]`). El catálogo sin filtrar es grande: recupéralo como máximo una vez al día y usa `name` u `offerTypes` para búsquedas específicas.
    </Note>

    <Accordion title="Opcional: obtén la mejor oferta única para un comercio (getOfferQuote)" icon="badge-percent">
      Si ya conoces el comercio y el monto, `getOfferQuote` devuelve directamente la mejor oferta disponible, incluyendo información de inventario en vivo.

      <CodeGroup>
        ```graphql Query theme={null}
        query getOfferQuote($input: GetOfferQuoteInput!) {
          getOfferQuote(input: $input) {
            offeringMerchantId
            offerId
            type
            hasStockInfo
            denominationsType
            termsAndConditions
            offerRates {
              maxUserRewardValue
              denominations
              allowedPaymentMethods
            }
            stockInfo {
              ... on StockInfoVariableType {
                __typename
                description
                maxDenomination
                minDenomination
              }
              ... on StockInfoFixedType {
                __typename
                denomination
                availableStock
              }
            }
          }
        }
        ```

        ```json Variables theme={null}
        {
          "input": {
            "slug": "starbucks",
            "denomination": 50.00,
            "paymentMethod": "FLUZPAY"
          }
        }
        ```
      </CodeGroup>

      `slug` y `denomination` son obligatorios. `paymentMethod` predetermina a `FLUZPAY` (tu saldo de Fluz) y también acepta `BANK_CARD`, `BANK_ACCOUNT`, `PAYPAL`, `APPLE_PAY` y `GOOGLE_PAY`.
    </Accordion>

    <Warning>
      Las tasas de cashback cambian con regularidad. Confirma siempre la tasa actual antes de comprar.
    </Warning>
  </Step>

  <Step title="Compra una tarjeta de regalo" icon="credit-card">
    Usa la mutación `purchaseGiftCard`. Puedes identificar qué comprar de dos maneras:

    <Tabs>
      <Tab title="Opción A — Slug del comercio (recomendado)">
        Pasa un `merchantSlug` y Fluz aplicará automáticamente la mejor oferta disponible para ese comercio.

        <CodeGroup>
          ```graphql Mutation theme={null}
          mutation purchaseGiftCard($input: PurchaseGiftCardInput!) {
            purchaseGiftCard(input: $input) {
              purchaseDisplayId
              purchaseAmount
              giftCard {
                giftCardId
                status
                termsAndConditions
              }
            }
          }
          ```

          ```json Variables theme={null}
          {
            "input": {
              "idempotencyKey": "0284be6f-1a69-44f7-9da0-5b5edaf45d19",
              "merchantSlug": "starbucks",
              "amount": 50.00,
              "balanceAmount": 50.00
            }
          }
          ```
        </CodeGroup>
      </Tab>

      <Tab title="Opción B — ID de oferta específico">
        Pasa un `offerId` que seleccionaste del catálogo para comprar esa oferta exacta.

        <CodeGroup>
          ```graphql Mutation theme={null}
          mutation purchaseGiftCard($input: PurchaseGiftCardInput!) {
            purchaseGiftCard(input: $input) {
              purchaseDisplayId
              purchaseAmount
              giftCard {
                giftCardId
                status
                termsAndConditions
              }
            }
          }
          ```

          ```json Variables theme={null}
          {
            "input": {
              "idempotencyKey": "0284be6f-1a69-44f7-9da0-5b5edaf45d19",
              "offerId": "0284be6f-1a69-44f7-9da0-5b5edaf45d19",
              "amount": 50.00,
              "balanceAmount": 50.00
            }
          }
          ```
        </CodeGroup>
      </Tab>
    </Tabs>

    #### Campos de entrada

    <ParamField body="idempotencyKey" type="string" required>
      Un UUID único generado por el cliente para que la compra se procese solo una vez.
    </ParamField>

    <ParamField body="offerId / merchantSlug" type="UUID / String" required>
      Proporciona **uno**. `merchantSlug` selecciona automáticamente la mejor tasa; `offerId` apunta a una oferta específica.
    </ParamField>

    <ParamField body="amount" type="Float" required>
      El monto de la tarjeta de regalo a comprar.
    </ParamField>

    <ParamField body="Funding source" type="UUID / Float">
      Cómo pagar. Usa `balanceAmount` (saldo de Fluz), `bankAccountId`, `bankCardId` o `paypalVaultId`. Puedes combinar tu saldo de Fluz con otra fuente.
    </ParamField>

    <ParamField body="defaultToBalance" default="true" type="Boolean">
      Recurre a tu saldo de Fluz si falla otro método de pago. Establécelo en `false` para deshabilitar ese fallback.
    </ParamField>

    <ParamField body="minRewardRate" type="Float">
      Tasa mínima de recompensa a aceptar al comprar vía `merchantSlug`.
    </ParamField>

    <ParamField body="exclusiveRateId" type="UUID">
      Fuerza una tasa exclusiva específica. Se encuentra en `getMerchants` para ofertas de tipo `EXCLUSIVE_RATE_OFFER`.
    </ParamField>

    <ParamField body="userCashBalanceId" type="UUID">
      La cuenta de gasto (saldo en efectivo) a cargar.
    </ParamField>

    <ParamField body="memo / transactionCategory / attachmentId" type="String / String / UUID">
      Metadatos de gasto opcionales. `memo` máximo 255 caracteres; las categorías se crean en el primer uso. Consulta [Agregar detalles de gasto](/features/add-expense-details).
    </ParamField>

    <Accordion title="Respuesta de ejemplo" icon="code">
      ```json theme={null}
      {
        "data": {
          "purchaseGiftCard": {
            "purchaseDisplayId": "1019688",
            "purchaseAmount": 50.00,
            "giftCard": {
              "giftCardId": "85de8b3e-4e72-462c-8ed1-a6f4982e22f7",
              "status": "ACTIVE",
              "termsAndConditions": "Except as required by law, Gift Cards cannot be transferred for..."
            }
          }
        }
      }
      ```
    </Accordion>

    <Info>
      Conserva el `giftCardId` de la respuesta: lo usarás para revelar la tarjeta en el siguiente paso. Si una compra falla, consulta [Códigos de error de tarjetas de regalo](/gift-card-error-codes).
    </Info>
  </Step>

  <Step title="Revela los detalles de la tarjeta de regalo" icon="eye">
    Finalmente, recupera los detalles canjeables (código, PIN y/o URL).

    <Accordion title="¿No tienes el giftCardId? Lista tus tarjetas primero (getGiftCards)" icon="list">
      Omite esto si acabas de capturar un `giftCardId` en el Paso 3. De lo contrario, lista tus tarjetas de regalo:

      ```graphql theme={null}
      query GetGiftCards {
        getGiftCards(paginate: { limit: 20, offset: 0 }) {
          giftCardId
          status
          createdAt
          deliveryFormat
          termsAndConditions
          merchant { merchantId name slug }
        }
      }
      ```

      Puedes filtrar con `status` y `userCashBalanceId`, y paginar con `paginate`.
    </Accordion>

    ### Revela los detalles de canje

    Llama a `revealGiftCardByGiftCardId` con el `giftCardId`.

    <CodeGroup>
      ```graphql Mutation theme={null}
      mutation RevealGiftCard($giftCardId: UUID!) {
        revealGiftCardByGiftCardId(giftCardId: $giftCardId) {
          code
          pin
          url
          termsAndConditions
        }
      }
      ```

      ```json Variables theme={null}
      {
        "giftCardId": "7c57c381-4b19-49e4-bbb0-404a45166ee4"
      }
      ```
    </CodeGroup>

    <Accordion title="Respuesta de ejemplo" icon="code">
      ```json theme={null}
      {
        "data": {
          "revealGiftCardByGiftCardId": {
            "code": "9877890000000000",
            "pin": "2014",
            "url": null,
            "termsAndConditions": "Except as required by law, Gift Cards cannot be transferred for..."
          }
        }
      }
      ```
    </Accordion>

    <Note>
      Los campos de canje varían por comercio. Algunas tarjetas devuelven un `code` alfanumérico sin `pin`; otras devuelven solo una `url`. Siempre representa según el `deliveryFormat` devuelto por **`getGiftCards`** (no `getMerchants`) — la oferta activa de un comercio puede cambiar después de la compra, y `getGiftCards` refleja el formato bajo el cual la tarjeta realmente se compró.
    </Note>

    <Warning>
      **¿Los detalles no se devuelven de inmediato?** Haz polling a `revealGiftCardByGiftCardId` con backoff exponencial: empieza en **300ms**, luego duplica (300 → 600 → 1200 → 2400ms…) hasta un retraso máximo de **180000ms (3 minutos)**. Detente tan pronto como regresen los detalles. Esto equilibra la rapidez con la carga y evita timeouts innecesarios.
    </Warning>
  </Step>
</Steps>

## ¡Listo! 🎉

Ejecutaste una transacción completa: financiastes un saldo, exploraste ofertas, compraste una tarjeta de regalo y la revelaste. A partir de aquí, explora el resto del API:

<CardGroup cols={2}>
  <Card title="Tarjetas virtuales" icon="credit-card" href="/features/virtual-cards">
    Emite y gestiona tarjetas virtuales aceptadas por la red.
  </Card>

  <Card title="Billeteras y transferencias" icon="wallet" href="/features/create-spend-accounts">
    Abre cuentas de gasto y mueve fondos entre ellas.
  </Card>

  <Card title="Actividad de transacciones" icon="receipt" href="/features/get-all-transactions">
    Extrae, filtra y anota el historial de transacciones.
  </Card>

  <Card title="Widgets embebidos" icon="book-copy" href="/developers/widgets">
    Integra flujos de Fluz directamente en tu propia UI.
  </Card>
</CardGroup>

<Note>
  **¿Quieres saber más?** Contáctanos en [support@fluz.app](mailto:support@fluz.app) para hablar con nuestros expertos o solicitar una demo.
</Note>
