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

# Your shop API

> Connect a custom store to Gleap: the endpoints Gleap calls, the JSON they return, and how Kai uses them to answer order questions, recommend products, start returns and share discount codes.

Use **Your shop API** when your store doesn't run on Shopify or WooCommerce. You add a few read endpoints to your backend that answer in the JSON shape below, and Kai can then:

* look up orders ("Where is my order?"),
* recommend products from your live catalogue,
* start returns, and
* check and share discount codes.

Kai shows the results as cards in the chat, and your team sees the customer's orders in the inbox.

Connect it under **Project settings → Integrations → Add integration → Your shop API**. Enter the base URL and the headers Gleap should send (for example an API key), then click **Test connection**.

## How Gleap calls your API

| | |
| - | - |
| Base URL | `https://…` only, on a public address. Gleap refuses private, internal and loopback addresses, also after redirects. |
| Headers | The headers you entered (up to 10), plus `Accept: application/json` and `User-Agent: Gleap-Commerce/1.0`. |
| Timeout | 10 seconds per request. |
| Response size | Up to 2 MB. |
| Status codes | `200` with JSON for results; `404` for "not found". Any other status counts as an error, and Kai tells the customer it can't reach the shop. |

All paths below are relative to your base URL. With the base URL `https://shop.example.com/api/gleap`, products are read from `https://shop.example.com/api/gleap/products`.

<Note>
  Gleap validates every answer. An entry that doesn't match the shape below is skipped, never half-used. Gleap also re-checks the results itself, so an order is only shown when its number and email really match.
</Note>

## Endpoints

| Method | Path | Required | Used for |
| - | - | - | - |
| `GET` | `/products?q=&max_price=&in_stock=true&limit=` | Yes | Product recommendations, **Test connection** |
| `GET` | `/products/{id}` | Yes | A single product |
| `GET` | `/orders/{number}?email=` | Yes | Order lookups by number |
| `GET` | `/customers?email=` or `/customers?user_id=` | Yes | Finding the customer |
| `GET` | `/orders?customer_id=&limit=` or `/orders?email=&limit=` | Yes | The customer's recent orders (Kai and the inbox) |
| `GET` | `/discounts/{code}` | Optional | Checking a discount code |
| `POST` | `/returns` | Optional | Starting a return |

### Products

`GET /products?q=hiking%20boots&max_price=120&in_stock=true&limit=6`

| Parameter | |
| - | - |
| `q` | Search words, in the shop's language. |
| `max_price` | Optional. Upper price limit in the shop currency. |
| `in_stock` | Optional. `true`: only products that can be bought now. |
| `limit` | Maximum number of products to return. Gleap asks for 6 or fewer. |

```json theme={null}
{
  "products": [
    {
      "id": "sku-4012",
      "title": "Trail Runner GTX",
      "subtitle": "Waterproof trail shoe",
      "image": "https://cdn.example.com/trail-runner.png",
      "price": { "amount": 89.9, "currency": "EUR" },
      "compareAtPrice": { "amount": 119, "currency": "EUR" },
      "rating": 4.6,
      "reviewCount": 212,
      "inStock": true,
      "url": "https://shop.example.com/products/trail-runner-gtx",
      "addToCartUrl": "https://shop.example.com/cart/add?sku=sku-4012",
      "variants": [
        { "id": "sku-4012-42", "title": "42 / Blue", "price": { "amount": 89.9, "currency": "EUR" }, "inStock": true, "stock": 4 }
      ]
    }
  ]
}
```

`GET /products/{id}` returns `{ "product": { … } }` in the same shape, or `404`.

| Field | Type | |
| - | - | - |
| `id` | string or number | Required. |
| `title` | string | Required. |
| `subtitle` | string | Shown under the title. |
| `image` | https URL | |
| `price`, `compareAtPrice` | [Money](#money) | `compareAtPrice` is shown struck through when it is higher than `price`. |
| `rating` | number, 0–5 | |
| `reviewCount` | integer | |
| `inStock` | boolean | |
| `url` | https URL | The product page. |
| `addToCartUrl` | https URL | Optional. A link that puts the product into the cart. The **+** button on the product card opens it. |
| `variants` | array | Optional: `id`, `title`, `price`, `inStock`, `stock`. |

### Orders

`GET /orders/1042?email=sam%40example.com`

The number is URL-encoded, without a leading `#`. `email` is the address the customer gave in the chat; when Gleap knows the signed-in customer, it is their email from `/customers`. You may return `404` when the email doesn't match. Gleap checks it again either way.

```json theme={null}
{
  "order": {
    "id": "ord_88123",
    "number": "1042",
    "email": "sam@example.com",
    "customerId": "cus_311",
    "status": "shipped",
    "createdAt": "2026-10-08T09:12:00Z",
    "currency": "EUR",
    "items": [
      {
        "id": "line_1",
        "title": "Trail Runner GTX",
        "variant": "42 / Blue",
        "image": "https://cdn.example.com/trail-runner.png",
        "quantity": 1,
        "price": { "amount": 89.9, "currency": "EUR" }
      }
    ],
    "subtotal": { "amount": 89.9, "currency": "EUR" },
    "shipping": { "amount": 4.9, "currency": "EUR" },
    "total": { "amount": 94.8, "currency": "EUR" },
    "fulfillments": [
      {
        "carrier": "DHL",
        "trackingNumber": "JD014600006281234567",
        "trackingUrl": "https://www.dhl.com/track?id=JD014600006281234567",
        "shippedAt": "2026-10-09T15:00:00Z",
        "estimatedDeliveryAt": "2026-10-15T16:00:00Z",
        "itemIds": ["line_1"]
      }
    ],
    "statusUrl": "https://shop.example.com/account/orders/1042"
  }
}
```

`GET /orders?customer_id=cus_311&limit=5` and `GET /orders?email=sam%40example.com&limit=5` return `{ "orders": [ … ] }`, newest first. Gleap drops any order that doesn't belong to that customer or email.

| Field | Type | |
| - | - | - |
| `number` | string or number | Required. What the customer knows ("1042"). |
| `id` | string | Your internal id. Defaults to `number`. |
| `email` | string | The order's email. Required for lookups by number and email. |
| `customerId` | string | The customer's `id` from `/customers`. |
| `status` | string | Required. One of `pending_payment`, `processing`, `on_hold`, `partially_shipped`, `shipped`, `out_for_delivery`, `delivered`, `cancelled`, `refunded`. |
| `createdAt` | ISO 8601 date | Required. |
| `items` | array | Required (may be empty). Each item: `id` and `title` (required), `quantity` (required, at least 1), plus `variant`, `image`, `price`, `productId` and `variantId`. The item `id` is what a return refers to. |
| `total` | [Money](#money) | Required. `subtotal`, `shipping` and `discount` are optional. |
| `fulfillments` | array | Shipments: `carrier`, `trackingNumber`, `trackingUrl`, `shippedAt`, `estimatedDeliveryAt`, `deliveredAt`, `itemIds`. |
| `statusUrl` | https URL | Your order status page, safe to give the customer. |

### Customers

`GET /customers?email=sam%40example.com` and `GET /customers?user_id=user_1842` return:

```json theme={null}
{
  "customers": [
    {
      "id": "cus_311",
      "email": "sam@example.com",
      "name": "Sam Rivera",
      "userId": "user_1842",
      "ordersCount": 7,
      "totalSpent": { "amount": 512.4, "currency": "EUR" },
      "createdAt": "2025-03-02T10:00:00Z"
    }
  ]
}
```

`user_id` is the user id your app passes to `Gleap.identify` with [identity verification](/documentation/guides/enforce-identity-verification). Return only the customer with exactly that user id, and include it as `userId`. Gleap drops any customer whose `userId` (or, for email lookups, `email`) doesn't match.

### Discount codes (optional)

`GET /discounts/SPRING10` returns `404` for an unknown code, or:

```json theme={null}
{
  "discount": {
    "valid": true,
    "code": "SPRING10",
    "headline": "10% off your next order",
    "expires": "2026-10-31T23:59:59Z",
    "applyUrl": "https://shop.example.com/discount/SPRING10"
  }
}
```

Return `"valid": false` for codes that are expired or used up. Kai only shares a code after this check says it is valid. `applyUrl` is optional: the **Apply to bag** button on the offer card opens it.

### Returns (optional)

Kai never starts a return on its own. It shows the customer a confirm card, and Gleap calls this endpoint once, when the customer taps **Start return** (or when a teammate approves a return an AI agent asked for).

`POST /returns`

```json theme={null}
{
  "orderNumber": "1042",
  "email": "sam@example.com",
  "items": [{ "id": "line_1", "quantity": 1 }],
  "reason": "Doesn't fit",
  "exchange": false,
  "reference": "Gleap ticket 6703c1f0e4b0a1b2c3d4e5f6"
}
```

```json theme={null}
{
  "return": {
    "returnId": "ret_5521",
    "labelUrl": "https://shop.example.com/returns/ret_5521/label.pdf",
    "message": "Return created, label attached."
  }
}
```

With a `labelUrl`, the customer gets the label link in the chat. Without one, Kai tells them your team emails the label.

### Money

```json theme={null}
{ "amount": 89.9, "currency": "EUR" }
```

`amount` is in major units (a number or a numeric string such as `"89.90"`). `currency` is the 3-letter ISO code. Gleap formats prices in the customer's language.

## Who sees order data

Products and discount codes are public. Orders are only shown to the order's customer:

* **Signed-in customers** (identity verification on): Gleap asks `/customers?user_id=` with the verified user id and shows that customer's orders.
* **Everyone else** has to give the **order number and the email used for the order**. Kai asks for both with a small form. A wrong email gets the same answer as an unknown number, and after 5 failed tries in one conversation Kai stops looking up orders.

Gleap never lists orders for an email alone, and never sends your headers or keys to the browser or into a chat card.

## Kai's switches

Each connection has four switches under **Integrations → Your shop API**:

| Switch | Default |
| - | - |
| Kai looks up orders | On |
| Kai recommends products | On |
| Kai starts returns (needs `POST /returns`) | Off |
| Kai shares discount codes (needs `GET /discounts/{code}`) | Off |

## Test connection

**Test connection** calls `GET /products?q=test&limit=3` with your headers. It passes when your API answers `200` with `{ "products": [ … ] }`; an empty list is fine.


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