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

# Overview

> Read the merchant catalog, understand fixed versus variable offers, buy a card, and reveal it — the whole gift card arc and which page to read for each step.

Buying a gift card through Fluz is four steps, and each one has a page of its own. This is the map.

<Steps>
  <Step title="Find the offer">
    Pull the catalog, or ask for the best rate on one merchant. → [Read the catalog](#reading-the-catalog)
  </Step>

  <Step title="Confirm the amount is purchasable">
    Fixed or variable, stocked or generated on demand. → [Fixed vs. variable](#fixed-vs-variable-offers)
  </Step>

  <Step title="Buy">
    One mutation, one card, one idempotency key. → [Buying](#buying-a-card)
  </Step>

  <Step title="Reveal">
    Get the code, PIN, or URL, and render it correctly. → [Revealing](#revealing-the-card)
  </Step>
</Steps>

<Warning>
  **Rates change constantly.** Fluz continuously re-prices to give customers the best available offer, and your rates are customized to your account. Never cache a rate and purchase against it later — re-confirm immediately before you buy.
</Warning>

***

## Vocabulary

Five terms do most of the work in this section, and three of them sound alike.

| Term                | What it is                                                                                                                |
| :------------------ | :------------------------------------------------------------------------------------------------------------------------ |
| **Merchant**        | The brand — Starbucks, Best Buy. Has a `merchantId` and a human-readable `slug`.                                          |
| **Offer**           | A specific purchasable deal from that merchant, with its own `offerId`. One merchant can have several.                    |
| **Offer rate**      | The reward attached to an offer — cashback, boosts, eligible denominations, permitted payment methods.                    |
| **Denomination**    | The face value of the card. Either chosen from a preset list or any amount in a range.                                    |
| **Delivery format** | How the card arrives: `URL`, `CODES`, `PIN_AS_CODE`, `PIN_WITH_URL`, or `CODE_WITH_PREFIX`. Determines how you render it. |

Two identifiers are easy to confuse: `merchantId` identifies the brand, `offeringMerchantId` identifies the party offering that particular deal. You buy against an `offerId` or a `slug`, never against a `merchantId`.

***

## Reading the catalog

Three ways in, for three different jobs.

| Approach                       | Query                                    | Use when                                                                                    |
| :----------------------------- | :--------------------------------------- | :------------------------------------------------------------------------------------------ |
| **Full catalog**               | `getMerchants`                           | You're building a browsable storefront, syncing a local catalog, or comparing across brands |
| **Best rate for one merchant** | `getOfferQuote`                          | You already know the brand and amount and just want today's best rate                       |
| **CSV export**                 | Dashboard → **Stores** → generate export | Analysis, finance, or a human wants a spreadsheet                                           |

Filter `getMerchants` to gift cards with `offerTypes: { giftCardOffer: true, cardLinkedOffer: false }`. Card-linked offers exist in the catalog but only gift card and exclusive offers are purchasable through the API today.

<Note>
  **The full catalog is a cached file, refreshed twice daily.** `getOfferQuote` is live. If the exact rate matters at purchase time — and it usually does — quote before you buy rather than trusting a catalog pull from this morning. Promotional rates are reflected in both, including in CSV exports generated during a promotion.
</Note>

Both queries are rate-limited, so paginate the full catalog rather than requesting it in one call. → [Get Catalog](/get-catalog) · [Get Gift Card Offers](/get-gift-card-offers) · [Get the Best Offer](/get-best-offer)

### Exclusive offers

Rates are customized per account. If yours has negotiated rates, they appear as offers with `type: "EXCLUSIVE_RATE_OFFER"` carrying an `exclusiveRateId`. Pass that ID to `purchaseGiftCard` to force the purchase onto that rate; omit it and Fluz picks the best available.

***

## Fixed vs. variable offers

This is the distinction that most shapes how you build, and a merchant can have both.

<CardGroup cols={2}>
  <Card title="FIXED" icon="lock">
    The card comes in **preset denominations** — $25, $50, \$100 — and you buy one of them exactly.

    Frequently backed by **real inventory** Fluz holds, which is why fixed offers usually carry **better rates and higher purchasing limits**.

    Inventory is finite. It runs out.
  </Card>

  <Card title="VARIABLE" icon="sliders-horizontal">
    You choose **any amount within a min/max range**, and the card is generated in real time.

    No inventory to deplete — effectively unlimited supply.

    Usually a **lower reward rate** than the same brand's fixed offer.
  </Card>
</CardGroup>

The practical trade-off: fixed offers pay better but can be exhausted mid-run; variable offers always work but pay less. High-volume ordering usually means taking fixed inventory first and falling back to variable.

### Where to read the purchasable amounts

Two fields decide this, and the combination determines which field holds the answer. Get this wrong and you'll submit amounts the offer can't fulfill.

| `denominationsType` | `hasStockInfo` | Purchasable amounts live in           | Meaning                                                  |
| :------------------ | :------------- | :------------------------------------ | :------------------------------------------------------- |
| `FIXED`             | `true`         | `stockInfo` → `StockInfoFixedType`    | Specific denominations with a countable `availableStock` |
| `FIXED`             | `false`        | `offerRates.denominations`            | Preset denominations, no inventory constraint published  |
| `VARIABLE`          | `true`         | `stockInfo` → `StockInfoVariableType` | A `minDenomination`–`maxDenomination` range              |
| `VARIABLE`          | `false`        | `offerRates.denominations`            | Any amount in the published range                        |

<Warning>
  `stockInfo` is a **union type**. You must query it with inline fragments for *both* shapes, or you'll get nothing back for one of them:

  ```graphql theme={null}
  stockInfo {
    ... on StockInfoFixedType    { __typename denomination availableStock }
    ... on StockInfoVariableType { __typename description minDenomination maxDenomination }
  }
  ```

  Always include both fragments, even when you think you know which one you'll get. See [How the GraphQL API works](/concepts/graphql).
</Warning>

Note that "has stock info" doesn't mean "has countable stock." On a variable offer, `stockInfo` returns a *range*, not a quantity. Only `StockInfoFixedType` carries an `availableStock` number you can decrement against.

Populating `stockInfo` requires Fluz to confirm inventory with the vendor, and vendor response times vary — so requesting it makes the query slower. Only ask for it when you're about to act on it. → [Get Inventory on Stocked Offers](/get-inventory)

***

## Buying a card

One mutation: `purchaseGiftCard`. Three decisions.

### 1. How to pick the offer

| You pass                         | Behavior                                                                                        |
| :------------------------------- | :---------------------------------------------------------------------------------------------- |
| `offerId`                        | **Pinned.** Buys that exact offer. If it's depleted, the call fails — no fallback.              |
| `merchantSlug`                   | **Auto-select.** Buys the best available rate for that brand, falling back as inventory shifts. |
| `merchantSlug` + `minRewardRate` | Auto-select **with a floor.** Fails rather than buying below your minimum rate.                 |
| `exclusiveRateId`                | Forces a specific negotiated rate.                                                              |

Pinning gives you certainty about the rate; auto-select gives you certainty about fulfillment. `merchantSlug` + `minRewardRate` is the middle ground and usually the right default for automated ordering.

### 2. How to pay

At least one funding source is required, and you can combine your Fluz balance with another.

| Field                                            | What it does                                                     |
| :----------------------------------------------- | :--------------------------------------------------------------- |
| `balanceAmount`                                  | **How much** to pay from your Fluz balance                       |
| `userCashBalanceId`                              | **Which spend account** that balance draws from                  |
| `bankCardId` / `bankAccountId` / `paypalVaultId` | External funding sources                                         |
| `defaultToBalance`                               | Fall back to balance if another method fails. Defaults to `true` |

<Warning>
  **If your account holds more than one spend account, always pass `userCashBalanceId` explicitly.** Omit it and Fluz draws from whichever account is flagged `isDefault` — which can change without your code changing, silently redirecting where your money comes from. This is the most common cause of surprise insufficient-funds failures.

  In automated pipelines, also set `defaultToBalance: false` so a purchase either draws from the account you named or fails cleanly.
</Warning>

### 3. Idempotency

`idempotencyKey` is required, and it's the difference between a retry and a double purchase. One key per intended card, reused on every retry of that same card. → [Idempotency](/docs/idempotency-requests)

→ [Purchase Gift Card](/purchase-gift-card)

### Buying more than one

**One call buys exactly one card**, on one offer, at one rate. There's no quantity field and no blending across offers. For ten cards, send ten calls with ten distinct idempotency keys.

What happens when you outrun inventory depends on how you picked the offer:

* **Pinned (`offerId`)** — once the stocked offer is depleted, remaining calls fail. No automatic fallback.
* **Auto-select (`merchantSlug`)** — remaining calls move to the next-best offer, often a variable one at a lower rate, unless `minRewardRate` blocks it.

→ [Purchase in Bulk](/purchase-in-bulk)

### Ordering at volume

Purchases against the same Fluz account process **sequentially**. Fire a large batch at once and calls queue behind each other, occasionally taking minutes to return.

<Note>
  **A client timeout is not a cancellation.** Fluz keeps processing a request you've stopped waiting for. Treat a timeout as an *unknown* outcome, never a failure.

  Resolve it by retrying with the **same** `idempotencyKey` — the retry returns the original purchase if it already succeeded, and won't double-charge. Issuing a fresh key for a purchase you already attempted is exactly how duplicate orders happen.

  Set client timeouts to about a minute, pace requests in waves rather than all at once, and spread heavy volume across multiple accounts.
</Note>

***

## Revealing the card

A purchase gives you a `giftCardId`. Redemption details come from a second call.

<Steps>
  <Step title="Get the gift card">
    Skip this if you just purchased and already hold the `giftCardId`. Otherwise `getGiftCards` lists them with `purchaseId`, `purchaseDisplayId`, `purchaseValue`, `currentValue`, and `status` — enough to reconcile orders without revealing every card.
  </Step>

  <Step title="Reveal it">
    `revealGiftCardByGiftCardId` returns `code`, `pin`, `url`, and `termsAndConditions`.
  </Step>
</Steps>

Three things that catch people out:

* **Not every card has all three fields.** Some merchants issue a code with no PIN; some issue only a URL. Fluz passes through whatever the merchant provides — handle nulls.
* **Render according to `deliveryFormat` and `barcodeType`,** and take `deliveryFormat` from `getGiftCards`, not from the merchant's current offer. Offers change; the card was issued under the format in force at purchase time. `barcodeType` is `NONE`, `C128`, `PDF417`, or `QRCODE`; when it's `NONE`, consider displaying the `faceplateUrl` instead.
* **Details may not be ready instantly.** Poll with exponential backoff — 300ms, doubling, capped at three minutes — and stop as soon as details return.

→ [View Gift Cards](/view-gift-card)

***

## Scopes

| Scope               | Needed for                           |
| :------------------ | :----------------------------------- |
| `LIST_OFFERS`       | `getMerchants`, `getOfferQuote`      |
| `PURCHASE_GIFTCARD` | `purchaseGiftCard`                   |
| `REVEAL_GIFTCARD`   | `revealGiftCardByGiftCardId`         |
| `LIST_PURCHASES`    | `getUserPurchases`, purchase history |
| `LIST_PAYMENT`      | `getUserCashBalances`, `getWallet`   |

Enable these on your app's **Permissions** tab before you build. A scope you request but haven't enabled is silently dropped. → [Configure OAuth App](/configure-o-auth-app)

***

## When things fail

| Code      | Meaning                                           |
| :-------- | :------------------------------------------------ |
| `GC-0002` | Purchase amount or Fluz Pay amount isn't positive |
| `GC-0003` | Couldn't retrieve the gift card record            |
| `GC-0004` | Purchase failed — try another payment method      |
| `GC-0006` | Couldn't reveal the card                          |

Full list: [Gift Card Error Codes](/gift-card-error-codes).

Before refunding an end user on a failed or timed-out purchase, **retry with the same `idempotencyKey` or look up the purchase by ID.** Timed-out requests frequently succeeded, and the code stays revealable until the purchase is refunded.

***

## Next steps

<CardGroup cols={2}>
  <Card title="Get catalog" icon="list" href="/get-catalog">
    Pull merchants and their offers.
  </Card>

  <Card title="Get the best offer" icon="badge-percent" href="/get-best-offer">
    Live quote for one merchant and amount.
  </Card>

  <Card title="Get inventory" icon="package" href="/get-inventory">
    Stock on fixed, stocked offers.
  </Card>

  <Card title="Purchase a gift card" icon="shopping-cart" href="/purchase-gift-card">
    The mutation, in full.
  </Card>

  <Card title="Purchase in bulk" icon="layers" href="/purchase-in-bulk">
    Ordering many cards, and depletion behavior.
  </Card>

  <Card title="View gift cards" icon="eye" href="/view-gift-card">
    Reveal codes, PINs, and URLs.
  </Card>
</CardGroup>
