> For the complete documentation index, see [llms.txt](https://docs.limio.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.limio.com/guides/headless-guides/guide-gift-purchase-and-redemption-with-the-api.md).

# Guide: Gift purchase and redemption with the API

Sell and redeem gift subscriptions from your own website with the Orders API.

### Overview

You can sell gift subscriptions on your own website. Limio continues to price, bill and fulfil them. This is a headless integration: your website owns the user interface, and your server calls the Limio API. Limio has no separate gift API. A gift uses the Orders API twice, with one check between the two orders:

1. **Purchase.** The purchaser pays. You submit a `new` order with `variant: "gift"`. Limio takes the payment and generates a gift code.
2. **Check.** The recipient gives you the code. You call `POST api/validate/gift/{code}`. Limio returns what the purchaser paid for.
3. **Redeem.** You submit a second `new` order with `payment.type: "gift_code"`. Limio creates the subscription for the recipient at no charge.

Limio generates the code, takes the payment, and refuses a code that is redeemed or expired. Your website does the rest: it shows the forms, sends the code to the recipient, and applies the rules in [Rules that your website applies](#rules-that-your-website-applies).

{% hint style="info" %}
If you use Limio pages and components, you do not need this guide. See [Guide: Subscription Gift Codes](/guides/feature-implementation-guides/guide-how-to-sell-subscription-gift-codes-in-limio-commerce.md).
{% endhint %}

### Prerequisites

* Access to the Limio Commerce API with a valid OAuth Bearer token. See [Authentication](https://docs.limio.com/developers/api/authentication-overview). Make all the calls in this guide from your server. Do not put the token in the browser.
* A published gift offer. On the offer, **This Is A Gift** and **This Is A Redeemable Gift** are true, and **Auto-Renew** is false. Step 1 of [Guide: Subscription Gift Codes](/guides/feature-implementation-guides/guide-how-to-sell-subscription-gift-codes-in-limio-commerce.md) shows the offer configuration.
* The `id` and `version` of that offer. Get them from [Get Offers V2](https://docs.limio.com/api/catalog-api/catalog#get-offers-v2).
* A payment method that your checkout collected, for example from a Zuora hosted payment page. Do not charge the payment method. When Limio processes the order, it takes the charge. A gift purchase accepts the same `payment` object as any other new order.
* A billing integration that supports gifts. See [Gift Offers with Zuora](https://docs.limio.com/integrations/zuora-integration/manage-your-pricing/how-to-sell-gift-products-with-limio-and-zuora) or [Gift subscriptions with Stripe](https://docs.limio.com/integrations/stripe-billing/gift-subscriptions).

***

### Step 1: Submit the gift purchase

Call `POST api/admin/order`. Two fields make the order a gift: `variant: "gift"` and `isGift: true`.

{% hint style="warning" %}
Always send `variant: "gift"`. When Limio processes the order, it does not read the gift attribute of the offer. Without `variant`, the order creates a standard subscription for the purchaser, and Limio generates no gift code.
{% endhint %}

```bash
curl -X POST 'https://your-environment.prod.limio.com/api/admin/order' \
  -H 'Authorization: Bearer <YOUR_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{
    "order_type": "new",
    "variant": "gift",
    "isGift": true,
    "process_immediately": true,
    "external_id": "GIFT-ORD-67890",
    "country": "GB",
    "orderItems": [
      {
        "offer": {
          "id": "8a512ee55befaea0f7fa962cea9982d0e84f0098",
          "version": "4dd34671f26fdbbf240560b78f7f2f54654cde72"
        },
        "quantity": 1
      }
    ],
    "customerDetails": {
      "firstName": "John",
      "lastName": "Doe",
      "email": "john.doe@example.com"
    },
    "billingDetails": {
      "firstName": "John",
      "lastName": "Doe",
      "address1": "1 High Street",
      "city": "London",
      "postalCode": "E14 7HG",
      "country": "GB"
    },
    "recipientDetails": {
      "firstName": "Alice",
      "lastName": "Johnson",
      "email": "alice.johnson@example.com",
      "startDate": "2026-12-25T00:00:00Z",
      "message": "Happy Christmas. Enjoy your subscription."
    },
    "payment": {
      "type": "zuora",
      "zuora": {
        "refId": "2c92c0f96e4e3b7f016e5f3b3b7b0b3d",
        "paymentGateway": "CreditCard_GB"
      }
    }
  }'
```

The order has two people in it:

* `customerDetails` and `billingDetails` describe the purchaser. The purchaser becomes the Limio customer and the billing account.
* `recipientDetails` describes the recipient. Send at least `email`, because you need it in Step 3. `startDate` is the date on which the purchaser wants the gift to arrive. Limio stores the date and the `message` on the order.
* `deliveryDetails` is the address of the recipient, for an offer with a physical product. You can omit it and collect the address at redemption.

Limio reads the price from the offer. You do not send a price. Limio charges the payment method in `payment` for the full amount now, in one charge. With Zuora, `payment.zuora.refId` is the ID of the Zuora payment method. To show the total and the tax before the purchase, send the same body to [Preview Order](https://docs.limio.com/api/orders-api/orders).

`external_id` is your reference for the order. If you send the same `external_id` again, Limio returns the first order and does not create a second one. After a timeout or a `502`, send the order again with the same `external_id`.

`process_immediately: true` makes Limio process the order before it responds. As a result, the gift code exists before you receive the response:

```json
{
  "id": "order-4dd195c3eee4297e33aecb5436cf8092",
  "order_reference": "8HK92PL401QW",
  "status": "submitted",
  "external_id": "GIFT-ORD-67890",
  "subscriptionReference": "2TJ25TK632EG"
}
```

If the response has an `error` field, Limio did not complete the order. Read `error_reason`, then the `payment` or `processing` section. See the [Submit Order reference](https://docs.limio.com/api/orders-api/orders) for the full request and response schema.

***

### Step 2: Get the gift code

The order response does not contain the gift code. Get the code from the objects related to the order. Use the `id` from Step 1.

```bash
curl -s 'https://your-environment.prod.limio.com/api/objects/limio/orders/order-4dd195c3eee4297e33aecb5436cf8092/related/gift_code' \
  -H 'Authorization: Bearer <YOUR_TOKEN>'
```

```json
{
  "items": [
    {
      "id": "gift_code-5f2a9c1e7b3d4860a1c2e3f4b5d6a7c8",
      "name": "k3TgR8mWq2Z",
      "record_type": "gift_code",
      "service": "limio"
    }
  ]
}
```

`name` is the gift code. A code has 11 letters and digits, and it is case-sensitive. Limio generates one code for each offer in the order. When an offer has a `quantity` of more than 1, that number of recipients can redeem the same code.

If `items` is empty, Limio did not process the order yet. Wait a few seconds, then do the lookup again.

To read the expiry date, get the gift code object with `GET api/objects/limio/gift_code/{id}`. Its `data` has `expires` and `redeemed`. A code expires 10 years after the purchase by default. You can change this period in **Settings > Gift code > Maximum Gift Code Age**.

***

### Step 3: Send the code to the recipient

Limio does not send emails. Your system sends the code to `recipientDetails.email` on `recipientDetails.startDate`, with a link to your redemption page. With a link such as `https://www.example.com/redeem?giftCode=k3TgR8mWq2Z`, your page can enter the code for the recipient.

To tell your other systems about gifts, subscribe to the `order/submitted` webhook. Limio sends it for a gift purchase and for a redemption. The payload is the order, and it does not contain the gift code. A purchase has `variant: "gift"`. A redemption has `payment.type: "gift_code"`. See [Webhooks](https://docs.limio.com/developers/webhooks/overview).

***

### Step 4: Check the gift code

When the recipient enters the code, call `POST api/validate/gift/{code}`. This call does not redeem the code, so you can repeat it.

```bash
curl -X POST 'https://your-environment.prod.limio.com/api/validate/gift/k3TgR8mWq2Z' \
  -H 'Authorization: Bearer <YOUR_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{}'
```

A valid code returns the items that the purchaser paid for, with every price set to zero:

```json
{
  "giftCode": "k3TgR8mWq2Z",
  "orderItems": [
    {
      "offer": {
        "id": "8a512ee55befaea0f7fa962cea9982d0e84f0098",
        "version": "4dd34671f26fdbbf240560b78f7f2f54654cde72"
      },
      "quantity": 1
    }
  ],
  "country": "GB",
  "recipientDetails": {
    "firstName": "Alice",
    "lastName": "Johnson",
    "email": "alice.johnson@example.com",
    "message": "Happy Christmas. Enjoy your subscription."
  },
  "deliveryDetails": { "country": "GB" },
  "total": { "amount": 179, "currency": "GBP" }
}
```

The example shows only the `id` and `version` of the offer. The response contains the full offer, so you can show its name and term to the recipient.

An invalid code also returns HTTP 200. Read `success` and `error.code`:

```json
{
  "success": false,
  "error": { "code": "LI-GC-102", "text": "Gift code has already been redeemed" }
}
```

| Code        | Meaning                                                       |
| ----------- | ------------------------------------------------------------- |
| `LI-GC-101` | The gift code does not exist                                  |
| `LI-GC-102` | The gift code is redeemed, or the purchaser received a refund |
| `LI-GC-103` | The gift code is expired                                      |

Write your own message for each code. Do not show `error.text` to the recipient.

If the recipient already has a subscription, send its ID as `subscriptionId` in the request body. The response then contains an `order_type`. [A recipient who already has a subscription](#a-recipient-who-already-has-a-subscription) shows how to use it. See the [Validate Gift Code reference](https://docs.limio.com/api/orders-api/orders) for the full schema.

***

### Step 5: Submit the redemption

Call `POST api/admin/order` again. Copy `orderItems` from the response in Step 4. Put the code in `giftCode` and in `payment.gift_code`.

```bash
curl -X POST 'https://your-environment.prod.limio.com/api/admin/order' \
  -H 'Authorization: Bearer <YOUR_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{
    "order_type": "new",
    "process_immediately": true,
    "country": "GB",
    "giftCode": "k3TgR8mWq2Z",
    "orderItems": [
      {
        "offer": {
          "id": "8a512ee55befaea0f7fa962cea9982d0e84f0098",
          "version": "4dd34671f26fdbbf240560b78f7f2f54654cde72"
        },
        "quantity": 1
      }
    ],
    "customerDetails": {
      "firstName": "Alice",
      "lastName": "Johnson",
      "email": "alice.johnson@example.com"
    },
    "recipientDetails": {
      "firstName": "Alice",
      "lastName": "Johnson",
      "email": "alice.johnson@example.com"
    },
    "deliveryDetails": {
      "country": "GB"
    },
    "payment": {
      "type": "gift_code",
      "gift_code": "k3TgR8mWq2Z"
    }
  }'
```

`customerDetails` now describes the recipient. The recipient becomes a second Limio customer, with a subscription of their own. Do not send `variant: "gift"` on this order.

`payment.type: "gift_code"` is the field that makes the order free. Limio sets every price to zero, takes no payment, and stores no payment method. Before Limio creates the subscription, it checks the code again.

{% hint style="warning" %}
Always copy `orderItems` from the response in Step 4. Limio does not compare the items of the redemption with the items of the gift purchase.
{% endhint %}

The subscription starts on the date of the redemption, and its term comes from the offer. A gift is one fixed term. The recipient has no payment method, so Limio cannot collect a renewal. At the end of the term, the recipient can renew into a standard paid subscription.

{% hint style="warning" %}
Keep **Auto-Renew** false on the gift offer. With Zuora, the subscription of the recipient takes its renewal setting from the offer. If the setting is true, Zuora renews the subscription and raises an invoice that has no payment method.
{% endhint %}

#### A recipient who already has a subscription

With `order_type: "new"`, a recipient who already subscribes gets a second subscription. To add the gift to the subscription that they have, do these steps:

1. Find the Limio subscription of the recipient with the [Objects API](https://docs.limio.com/api/objects-api).
2. In Step 4, send `{ "subscriptionId": "<SUBSCRIPTION_ID>" }` as the request body.
3. In Step 5, send the `order_type` and the `orderItems` from that response. Add `subscriptionId`. Keep `giftCode` and `payment`.

| `order_type` | When Limio returns it                         | What the redemption does                                                                                      |
| ------------ | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `renew`      | The subscription does not renew automatically | Adds the gift term, at no charge, from the end date of the current term                                       |
| `credit`     | The subscription renews automatically         | Puts the value of the gift on the billing account as a payment. Also send `total` from the response in Step 4 |

Limio checks the code again only for a `new` order. For `renew` and `credit`, always do Step 4 immediately before Step 5.

***

### Rules that your website applies

The Limio gift components apply the rules in this table in the browser. The API does not apply them, so an order that breaks one of them still succeeds. If a rule is relevant to your business, apply it on your website.

| Rule in the Limio components                                                                                                                                                                      | Where the value comes from                                | API enforces it |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | --------------- |
| The gift code is valid, not redeemed, and not expired                                                                                                                                             | The gift code                                             | Yes             |
| A redemption is free                                                                                                                                                                              | `payment.type: "gift_code"`                               | Yes             |
| Limio refuses a redemption after the code reaches its quantity                                                                                                                                    | The gift code                                             | Yes             |
| The code matches `^[a-zA-Z0-9]{10,11}$` before the check in Step 4. The components do not trim the code or change its case                                                                        | Redeem component setting                                  | No              |
| Purchase of a gift with a physical product: the billing country is one of the allowed countries of the offer. Tax comes from the billing address. The country of the recipient has no restriction | Offer attribute `allowed_countries__limio`                | No              |
| Redemption of a gift with a physical product: the delivery country is in the delivery allow list                                                                                                  | **Settings > Localisation > Delivery country allow list** | No              |
| Redemption: the country is not in the disallowed list                                                                                                                                             | **Settings > Localisation > Disallowed country list**     | No              |
| Redemption of a digital gift: the recipient gives a country. For `US` and `CA`, the recipient also gives a state and a postal code                                                                | Fixed in the component                                    | No              |
| The first name, the last name and the email of the recipient are mandatory. Each has a maximum of 35 characters                                                                                   | Checkout component settings                               | No              |
| The gift message has a maximum of 255 characters                                                                                                                                                  | Fixed in the component                                    | No              |
| `recipientDetails.startDate` is between today and one year from today                                                                                                                             | Fixed in the component                                    | No              |
| A recipient with an active subscription sees a warning before the redemption, and can continue                                                                                                    | Block Gift Purchase Dialog component                      | No              |

The offer attribute is in the response of [Get Offers V2](https://docs.limio.com/api/catalog-api/catalog#get-offers-v2). The two Localisation lists are not available from the API. Keep a copy of them in your own configuration. When the Limio setting changes, change the copy.

Limio has no rule that stops a purchaser who sends a gift to their own email address.

***

### Related resources

* [Guide: Refund a gift subscription](/guides/headless-guides/guide-refund-a-gift-subscription.md): refund a gift that the recipient did not redeem
* [Guide: Subscription Gift Codes](/guides/feature-implementation-guides/guide-how-to-sell-subscription-gift-codes-in-limio-commerce.md): gift offer configuration, and the objects that Limio creates at purchase and at redemption
* [Orders API reference](https://docs.limio.com/api/orders-api/orders): Submit Order and Validate Gift Code request and response schemas
* [Gift subscriptions with Stripe](https://docs.limio.com/integrations/stripe-billing/gift-subscriptions): the limits of gifts on Stripe Billing

When you contact support about a gift, include the `order_reference` of the purchase and the gift code.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

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

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.limio.com/guides/headless-guides/guide-gift-purchase-and-redemption-with-the-api.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
