# Headless implementation

Build your own product browsing and purchasing experience using the Glu API, while Glu handles the product catalogue, gift card issuance, membership management, and fulfilment.

This guide walks through using the Organisation API to fetch products and their variants, display them on your website, and complete purchases — all from your own frontend and backend.

### Overview

A headless integration typically follows this flow:

1. **Fetch store configuration** — get currency, validity settings, and store details.
2. **List products** — retrieve your catalogue with images, descriptions, and configurable options.
3. **Fetch variants and pricing** — get prices and stock for each product variant.
4. **Collect payment** — handle payment on your side (your own Stripe, payment gateway, etc.).
5. **Issue the product** — call the Glu API to issue the gift card or apply the membership.

```
Your Website                    Your Server                     Glu API
     │                               │                              │
     │── Browse products ────────────►│                              │
     │                               │── GET /products ────────────►│
     │                               │◄── Product catalogue ───────│
     │◄── Render product grid ──────│                              │
     │                               │                              │
     │── Select variant ────────────►│                              │
     │                               │── GET /products/{id}/       │
     │                               │   variants ────────────────►│
     │                               │◄── Variants + pricing ─────│
     │◄── Show options + price ─────│                              │
     │                               │                              │
     │── Complete checkout ─────────►│                              │
     │                               │── (charge payment) ────────►│ Your gateway
     │                               │◄── Payment confirmed ──────│
     │                               │                              │
     │                               │── POST /gift_cards ────────►│ Glu API
     │                               │◄── Gift card issued ───────│
     │◄── Order confirmation ──────│                              │
```

### Step 1: Fetch your store configuration

Start by retrieving your store to understand the currency, validity settings, and terms that apply to products sold through it.

<mark style="color:green;">`GET`</mark>[`/stores`](/docs/api-reference/organisation-api/store.md)

{% code title="Example response" lineNumbers="true" %}

```json
[
  {
    "id": 1,
    "name": "My Gift Card Store",
    "status": "Active",
    "domain": "gifts.example.com",
    "currencyCode": "GBP",
    "validityPeriod": "Years",
    "validityLength": 1,
    "giftCardsExpire": true,
    "defaultVoucherTerms": "Valid for 12 months from date of purchase.",
    "defaultVoucherRedeemInstructions": "Present at checkout.",
    "termsUrl": "https://example.com/terms",
    "privacyUrl": "https://example.com/privacy",
    ...
  }
]
```

{% endcode %}

Cache the store configuration — it changes infrequently. Use the `id` when issuing gift cards later.

### Step 2: List products

`GET` [`/products`](/docs/api-reference/organisation-api/product.md)

{% code title="Example response" lineNumbers="true" expandable="true" %}

```json
[
  {
    "id": 12,
    "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "£50 Gift Card",
    "type": "Voucher",
    "description": "The perfect gift for any occasion.",
    "status": "Active",
    "permalink": "50-gift-card",
    "images": [
      "https://cdn.example.com/images/gift-card-50.jpg"
    ],
    "store": 1,
    "visibility": "Public",
    "productConfigurableOptions": [],
    "PriceSuffix": "",
    "variants": [101],
    "minimumPricing": [
      {
        "price": 5000,
        "currencyCode": "GBP"
      }
    ],
    "productCategories": [
      {
        "id": 1,
        "name": "Gift Cards",
        "position": 0
      }
    ],
    "scheduledFrom": null,
    "scheduledTo": null,
    "deleted": false
  },
  {
    "id": 13,
    "name": "Custom Amount Gift Card",
    "type": "Voucher",
    "description": "Choose your own amount.",
    "status": "Active",
    "visibility": "Public",
    "productConfigurableOptions": [
      {
        "id": 1,
        "name": "Delivery",
        "values": [
          {"id": 1, "name": "Email"},
          {"id": 2, "name": "Post"}
        ]
      }
    ],
    "variants": [102, 103],
    "minimumPricing": [
      {
        "price": 1000,
        "currencyCode": "GBP"
      }
    ]
  }
]
```

{% endcode %}

#### Key product properties

| Property                     | Description                                                                                           |
| ---------------------------- | ----------------------------------------------------------------------------------------------------- |
| `type`                       | `Voucher` (gift card), `Physical` (physical gift), `RecurringProduct` (membership), `ReferralProgram` |
| `status`                     | Only display `Active` products. `Scheduled` products have future `scheduledFrom` dates                |
| `visibility`                 | `Public` products are listed openly; `Direct Link` are accessible only via permalink                  |
| `images`                     | Array of image URLs for the product                                                                   |
| `variants`                   | Array of variant IDs — fetch separately for pricing                                                   |
| `productConfigurableOptions` | Options the customer can choose (e.g. delivery method, design)                                        |
| `minimumPricing`             | Lowest price across variants per currency — useful for "From £10" labels                              |
| `PriceSuffix`                | Text appended to the price display (e.g. "/month" for memberships)                                    |
| `permalink`                  | URL-safe slug for building product page URLs                                                          |

#### Filtering products for display

When building your product listing page:

* **Only show `status: Active`** — filter out `Draft`, `Expired`, `Deleted`, and future `Scheduled` products.
* **Respect `visibility`** — `Direct Link` products should only be shown when accessed via their permalink, not in the main listing.
* **Check scheduling** — if `scheduledFrom` is set and in the future, the product isn't yet available.
* **Filter by `deleted: false`** — deleted products remain in the API response for historical reference.

### Step 3: Fetch variants and pricing

`GET` [`/products/{productId}/variants`](/docs/api-reference/organisation-api/product.md#get-products-productid-variants)

Each product has one or more variants. Variants carry the actual pricing, stock levels, and selected option combinations.

{% code title="" lineNumbers="true" %}

```json
[
  {
    "id": 101,
    "selectedOptions": [],
    "pricing": [
      {
        "id": 1,
        "price": 5000,
        "storeCurrency": {
          "id": 1,
          "currencyCode": "GBP"
        }
      },
      {
        "id": 2,
        "price": 6000,
        "storeCurrency": {
          "id": 2,
          "currencyCode": "EUR"
        }
      }
    ],
    "stockQuantity": null,
    "chargeTax": true,
    "deleted": false
  }
]
```

{% endcode %}

For a product with configurable options (e.g. delivery method):

```json
[
  {
    "id": 102,
    "selectedOptions": [
      {
        "id": 1,
        "product_configurable_option": {
          "id": 1,
          "name": "Delivery"
        },
        "name": "Email"
      }
    ],
    "pricing": [
      {
        "id": 3,
        "price": 0,
        "storeCurrency": {
          "id": 1,
          "currencyCode": "GBP"
        }
      }
    ],
    "stockQuantity": null,
    "chargeTax": true,
    "deleted": false
  },
  {
    "id": 103,
    "selectedOptions": [
      {
        "id": 2,
        "product_configurable_option": {
          "id": 1,
          "name": "Delivery"
        },
        "name": "Post"
      }
    ],
    "pricing": [
      {
        "id": 4,
        "price": 500,
        "storeCurrency": {
          "id": 1,
          "currencyCode": "GBP"
        }
      }
    ],
    "stockQuantity": 50,
    "chargeTax": true,
    "deleted": false
  }
]
```

#### Understanding variant data

| Property          | Description                                                                        |
| ----------------- | ---------------------------------------------------------------------------------- |
| `pricing`         | Array of prices per currency. Amounts in **smallest denomination** (5000 = £50.00) |
| `selectedOptions` | Which configurable option values this variant represents                           |
| `stockQuantity`   | `null` = unlimited stock. A number = remaining stock. Show "out of stock" when 0   |
| `chargeTax`       | Whether tax should be applied on top of the price                                  |
| `deleted`         | Filter out `true` — deleted variants remain for historical orders                  |

#### Matching variants to options

When a product has configurable options, you need to map the customer's selection to the correct variant:

1. Display the `productConfigurableOptions` from the product as dropdown menus or option selectors.
2. When the customer selects options, find the variant whose `selectedOptions` match their choices.
3. Display that variant's `pricing` for the customer's currency.

### Step 4: Collect payment

Handle payment on your side using your own payment gateway (Stripe, Adyen, etc.). The Glu API doesn't process payments in headless mode — you charge the customer, then issue the product via the API.

Your checkout flow should collect:

* **Customer details**: name, email address (required for gift card delivery and contact creation).
* **Recipient details** (if gifting): recipient name, email, optional personal message.
* **Variant selection**: the chosen variant ID based on the customer's option selections.
* **Payment**: charge the variant price plus any applicable tax or processing fees.

### Step 5: Issue the product

After payment is confirmed, call the appropriate Glu API endpoint to create the product.

#### Issuing a gift card

`POST` [`/gift_cards`](/docs/api-reference/organisation-api/gift-card.md#post-gift_cards)

{% code title="" lineNumbers="true" %}

```json
{
  "storeId": 1,
  "productVariantId": 101,
  "contactId": 1042,
  "amount": 5000,
  "currencyCode": "GBP",
  "personalMessage": "Happy Birthday!"
}
```

{% endcode %}

| Field              | Required | Description                                                    |
| ------------------ | -------- | -------------------------------------------------------------- |
| `storeId`          | Yes      | The store to issue under (from step 1)                         |
| `productVariantId` | No       | Use to inherit settings from an existing product variant       |
| `name`             | No       | Required if no `productVariantId` — the gift card display name |
| `description`      | No       | Gift card description                                          |
| `terms`            | No       | Terms and conditions                                           |
| `contactId`        | No       | Assign to an existing contact                                  |
| `amount`           | No       | Initial balance in smallest denomination                       |
| `currencyCode`     | No       | 3-letter currency code (e.g. `GBP`, `USD`, `EUR`)              |
| `personalMessage`  | No       | Message from the purchaser to the recipient                    |
| `code`             | No       | Custom code — if omitted, one is auto-generated                |

**Two approaches:**

1. **From a product variant** — provide `productVariantId` and the gift card inherits the product's name, description, terms, validity, and other settings. This is the recommended approach for storefront purchases.
2. **Manually** — omit `productVariantId` and provide `name` (required), plus optionally `description`, `terms`, `amount`, etc. Useful for programmatic issuance outside of a product context.

Response:

```json
{
  "id": 5678,
  "status": "Valid",
  "name": "£50 Gift Card",
  "class": "Monetary",
  "validFrom": "2026-04-07T14:30:00+00:00",
  "expiresAt": "2027-04-07T23:59:59+00:00",
  "partiallyRedeemable": true,
  "terms": "Valid for 12 months from date of purchase.",
  "personalMessage": "Happy Birthday!",
  "redeemInstructions": "Present at checkout.",
  "description": "The perfect gift for any occasion."
}
```

#### Applying a membership

`POST /subscriptions`

For `RecurringProduct` type products, apply the membership tier to a contact:

```json
{
  "contactId": 1042,
  "recurringProductTierId": 5,
  "startedAt": "2026-04-07T00:00:00+00:00"
}
```

This creates an active subscription linking the contact to the membership tier.

#### Creating the contact

If the customer doesn't already exist as a contact, create one first:

`POST /contacts`

```json
{
  "emailAddress": "jane@example.com",
  "firstName": "Jane",
  "lastName": "Smith"
}
```

This endpoint uses find-or-create semantics — if a contact with the same email already exists, it updates and returns the existing record. Use the returned `id` as the `contactId` when issuing the gift card or membership.

### Putting it all together

Here's a complete example flow for a gift card purchase:

```bash
# 1. Fetch products (cache this, refresh periodically)
curl -s -H "x-api-key: $GLU_API_KEY" \
  "https://api.glu.io/products" | jq '.[] | select(.status == "Active")'

# 2. Fetch variants for the selected product
curl -s -H "x-api-key: $GLU_API_KEY" \
  "https://api.glu.io/products/12/variants"

# 3. Customer completes checkout on your site...
#    You charge their card via your payment gateway...

# 4. Create or find the contact
curl -X POST -H "x-api-key: $GLU_API_KEY" \
  -H "Content-Type: application/json" \
  "https://api.glu.io/contacts" \
  -d '{
    "emailAddress": "jane@example.com",
    "firstName": "Jane",
    "lastName": "Smith"
  }'
# Returns: {"id": 1042, ...}

# 5. Issue the gift card
curl -X POST -H "x-api-key: $GLU_API_KEY" \
  -H "Content-Type: application/json" \
  "https://api.glu.io/gift_cards" \
  -d '{
    "storeId": 1,
    "productVariantId": 101,
    "contactId": 1042,
    "amount": 5000,
    "currencyCode": "GBP",
    "personalMessage": "Happy Birthday!"
  }'
# Gift card is issued, email delivery triggered automatically
```

### Caching and performance

Products and store configuration change infrequently. Recommended caching strategy:

| Data                 | Cache duration | Refresh trigger                                       |
| -------------------- | -------------- | ----------------------------------------------------- |
| Store configuration  | 1 hour         | Manual flush on settings change                       |
| Products list        | 5–15 minutes   | Use `updatedAt[after]` filter for incremental updates |
| Variants and pricing | 5–15 minutes   | Same as products                                      |
| Contacts             | Don't cache    | Always query fresh before issuing                     |

Use the date filters (`updatedAt[after]`, `createdAt[after]`) to efficiently check for changes since your last fetch rather than re-downloading the full catalogue.

### Handling multiple currencies

If your store supports multiple currencies, each variant will have multiple entries in its `pricing` array — one per currency. Match the customer's selected currency to the correct pricing entry:

When issuing the gift card, always include `currencyCode` to ensure the balance is set in the correct currency.

### Handling configurable options

Products with configurable options (like delivery method or design choice) require matching the customer's selections to the correct variant:

Always validate stock before checkout — if `stockQuantity` is `0`, the variant is sold out. Check again server-side before issuing, as stock may have changed between page load and checkout.

### Tips

* **Use `productVariantId`** when issuing gift cards from your storefront — it inherits all settings from the product configuration, keeping your admin users in control.
* **Create contacts early** — use `POST /contacts` before issuing so the gift card is linked to its recipient from the start.
* **Prices are always in smallest denomination** — divide by 100 for display (for most currencies).
* **Filter out deleted and draft products** — the API returns everything; filter to `status: Active` and `deleted: false` on your side.
* **Pagination** — product lists paginate with the `page` query parameter. Iterate until the response returns an empty array.
* **Stock checking** — a `stockQuantity` of `null` means unlimited stock. Only check for sold-out when it's a number.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://glu.gitbook.io/docs/use-cases/display-and-sell-on-your-website/headless-implementation.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
