> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thanx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Guest Universal Promo Codes

> Apply a Universal Promo Code to a basket without an end-user, using a guest access token minted from your Merchant-owned OAuth app.

A promo code can also be applied without an end-user — a **guest basket**. Instead
of an end-user access token, a guest basket authenticates with a short-lived,
ownerless Bearer token minted from your Merchant-owned OAuth app credentials.

> [!NOTE]
>
> Guest baskets never accrue loyalty points or progress — there is no member to
> credit. Only OAuth apps owned by your Merchant account are eligible to mint
> guest tokens; apps shared across merchants (Partner, whitelabel, or global
> apps) cannot. This flow must be enabled for your account by Thanx.

## Minting a guest token

Exchange your Merchant-owned OAuth app's `client_id` / `client_secret` (in the
JSON body, or as HTTP Basic) for a guest access token with
[Create Guest Token](https://docs.thanx.com/loyalty/create-guest-token.md)
(`POST /loyalty-api/oauth/token`, `grant_type=client_credentials`). The token
lasts `1800` seconds (30 minutes) and has no refresh token — re-mint once it
expires. Keep the `client_secret` on your server; never ship it in a client or
mobile app build.

## Guest basket requests

Once minted, use the guest token exactly like a member's Bearer token on
[Create & Update Basket](https://docs.thanx.com/loyalty/create-update-basket.md) — the basket shape and lifecycle are
unchanged. The only differences: a guest basket must include a `promo_code` and
can never carry `rewards` or `points_products`, and a guest token only works on
`POST /baskets` — not on
[Get Account](https://docs.thanx.com/loyalty/get-account.md) or
[Qualifying Rewards for a Basket](https://docs.thanx.com/loyalty/qualifying-baskets.md).

```bash Guest basket (promo code) theme={null}
curl https://loyalty.thanxsandbox.com/api/baskets \
  -X POST \
  -H "Authorization: Bearer ${guest_token}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/vnd.thanx-v1+json" \
  -H "Merchant-Key: ${merchant_key}" \
  -d '{
    "id": "order-abc-123",
    "state": "billed",
    "order_timestamp": "2026-06-08T18:02:05Z",
    "location_uid": "LG-567fhwer",
    "promo_code": "SUMMER10",
    "payments": [
      {
        "issuer": "visa",
        "last4": "1234",
        "amount": 20.00,
        "authorized_at": "2026-06-07T18:02:05Z"
      }
    ],
    "items": [
      {
        "id": "ng-23492",
        "name": "Cheese Pizza",
        "price": 20.00,
        "categories": [
          "pizza"
        ]
      }
    ],
    "subtotal": 20.00
  }'
```

## Guest basket errors

Promo code rejections on a guest basket are deliberately generic: whether the
code is unknown, expired, fully redeemed, not valid at this location or time, or
the order minimum is not met, the response is always the same generic message,
`This promo code is not valid`. A `promo_code` that fails format validation (longer than 64 characters,
or containing control characters) is rejected earlier with a standard `400`
validation error instead.

| Condition | Status | Response |
| - | - | - |
| Promo code not accepted (unknown, expired, fully redeemed, not valid here or now, minimum not met) | 400 | `{"code": 400, "message": "This promo code is not valid"}` |
| `promo_code` missing or empty | 401 | `{"code": 401, "message": "There was an error authenticating you"}` |
| `rewards` or `points_products` present | 401 | same as above |
| Guest token used on any endpoint other than `POST /baskets` (e.g. [Get Account](https://docs.thanx.com/loyalty/get-account.md) or [Qualifying Rewards for a Basket](https://docs.thanx.com/loyalty/qualifying-baskets.md)) | 401 | same as above |
| Guest flow not enabled for the token's merchant | 401 | same as above |
| Guest token expired — [mint a new token](https://docs.thanx.com/loyalty/create-guest-token.md) | 401 | same as above |
| Basket `id` missing | 400 | `{"code": 400, "message": "A basket id is required when applying a promo code"}` |
| `Merchant-Key` missing, invalid, or does not match the token's merchant | 401 | `{"code": 401, "message": "The Merchant-Key header is missing or invalid"}` |
| Too many guest requests for this merchant | 429 | Plain-text `Retry later`, with a `Retry-After` header (seconds) |

> [!NOTE]
>
> Guest traffic is rate limited per merchant, not per shopper — your server
> makes these calls, so Thanx cannot tell your shoppers apart. Rate-limit individual shoppers on your side, and on a `429`
> wait the `Retry-After` seconds before retrying.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.