---
title: "Your shop API"
description: "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."
url: https://docs.gleap.ai/en/articles/715-your-shop-api
collection: "Guides"
updated: 2026-11-01
language: en
---

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

Open Settings → Apps → Add app and choose Your shop API. Review its uses and continue to setup. Enter the HTTPS base URL and headers Gleap should send, then use Test connection before connecting it.

## How Gleap calls your API

<table style="min-width: 50px;"><colgroup><col style="min-width: 25px;"><col style="min-width: 25px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p></p></th><th colspan="1" rowspan="1"><p></p></th></tr><tr><td colspan="1" rowspan="1"><p>Base URL</p></td><td colspan="1" rowspan="1"><p><code>https://…</code> only, on a public address. Gleap refuses private, internal and loopback addresses, also after redirects.</p></td></tr><tr><td colspan="1" rowspan="1"><p>Headers</p></td><td colspan="1" rowspan="1"><p>The headers you entered (up to 10), plus <code>Accept: application/json</code> and <code>User-Agent: Gleap-Commerce/1.0</code>.</p></td></tr><tr><td colspan="1" rowspan="1"><p>Timeout</p></td><td colspan="1" rowspan="1"><p>10 seconds per request.</p></td></tr><tr><td colspan="1" rowspan="1"><p>Response size</p></td><td colspan="1" rowspan="1"><p>Up to 2 MB.</p></td></tr><tr><td colspan="1" rowspan="1"><p>Status codes</p></td><td colspan="1" rowspan="1"><p><code>200</code> with JSON for results; <code>404</code> for "not found". Any other status counts as an error, and Kai tells the customer it can't reach the shop.</p></td></tr></tbody></table>

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.

## Endpoints

<table style="min-width: 100px;"><colgroup><col style="min-width: 25px;"><col style="min-width: 25px;"><col style="min-width: 25px;"><col style="min-width: 25px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Method</p></th><th colspan="1" rowspan="1"><p>Path</p></th><th colspan="1" rowspan="1"><p>Required</p></th><th colspan="1" rowspan="1"><p>Used for</p></th></tr><tr><td colspan="1" rowspan="1"><p><code>GET</code></p></td><td colspan="1" rowspan="1"><p><code>/products?q=&amp;max_price=&amp;in_stock=true&amp;limit=</code></p></td><td colspan="1" rowspan="1"><p>Yes</p></td><td colspan="1" rowspan="1"><p>Product recommendations, <strong>Test connection</strong></p></td></tr><tr><td colspan="1" rowspan="1"><p><code>GET</code></p></td><td colspan="1" rowspan="1"><p><code>/products/{id}</code></p></td><td colspan="1" rowspan="1"><p>Yes</p></td><td colspan="1" rowspan="1"><p>A single product</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>GET</code></p></td><td colspan="1" rowspan="1"><p><code>/orders/{number}?email=</code></p></td><td colspan="1" rowspan="1"><p>Yes</p></td><td colspan="1" rowspan="1"><p>Order lookups by number</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>GET</code></p></td><td colspan="1" rowspan="1"><p><code>/customers?email=</code> or <code>/customers?user_id=</code></p></td><td colspan="1" rowspan="1"><p>Yes</p></td><td colspan="1" rowspan="1"><p>Finding the customer</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>GET</code></p></td><td colspan="1" rowspan="1"><p><code>/orders?customer_id=&amp;limit=</code> or <code>/orders?email=&amp;limit=</code></p></td><td colspan="1" rowspan="1"><p>Yes</p></td><td colspan="1" rowspan="1"><p>The customer's recent orders (Kai and the inbox)</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>GET</code></p></td><td colspan="1" rowspan="1"><p><code>/discounts/{code}</code></p></td><td colspan="1" rowspan="1"><p>Optional</p></td><td colspan="1" rowspan="1"><p>Checking a discount code</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>POST</code></p></td><td colspan="1" rowspan="1"><p><code>/returns</code></p></td><td colspan="1" rowspan="1"><p>Optional</p></td><td colspan="1" rowspan="1"><p>Starting a return</p></td></tr></tbody></table>

### Products

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

<table style="min-width: 50px;"><colgroup><col style="min-width: 25px;"><col style="min-width: 25px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Parameter</p></th><th colspan="1" rowspan="1"><p></p></th></tr><tr><td colspan="1" rowspan="1"><p><code>q</code></p></td><td colspan="1" rowspan="1"><p>Search words, in the shop's language.</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>max_price</code></p></td><td colspan="1" rowspan="1"><p>Optional. Upper price limit in the shop currency.</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>in_stock</code></p></td><td colspan="1" rowspan="1"><p>Optional. <code>true</code>: only products that can be bought now.</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>limit</code></p></td><td colspan="1" rowspan="1"><p>Maximum number of products to return. Gleap asks for 6 or fewer.</p></td></tr></tbody></table>

```json
{
  "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`.

<table style="min-width: 75px;"><colgroup><col style="min-width: 25px;"><col style="min-width: 25px;"><col style="min-width: 25px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Field</p></th><th colspan="1" rowspan="1"><p>Type</p></th><th colspan="1" rowspan="1"><p></p></th></tr><tr><td colspan="1" rowspan="1"><p><code>id</code></p></td><td colspan="1" rowspan="1"><p>string or number</p></td><td colspan="1" rowspan="1"><p>Required.</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>title</code></p></td><td colspan="1" rowspan="1"><p>string</p></td><td colspan="1" rowspan="1"><p>Required.</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>subtitle</code></p></td><td colspan="1" rowspan="1"><p>string</p></td><td colspan="1" rowspan="1"><p>Shown under the title.</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>image</code></p></td><td colspan="1" rowspan="1"><p>https URL</p></td><td colspan="1" rowspan="1"><p></p></td></tr><tr><td colspan="1" rowspan="1"><p><code>price</code>, <code>compareAtPrice</code></p></td><td colspan="1" rowspan="1"><p><a target="_blank" rel="noopener noreferrer nofollow" class="linktype-link link" data-linktype="link" href="#money">Money</a></p></td><td colspan="1" rowspan="1"><p><code>compareAtPrice</code> is shown struck through when it is higher than <code>price</code>.</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>rating</code></p></td><td colspan="1" rowspan="1"><p>number, 0–5</p></td><td colspan="1" rowspan="1"><p></p></td></tr><tr><td colspan="1" rowspan="1"><p><code>reviewCount</code></p></td><td colspan="1" rowspan="1"><p>integer</p></td><td colspan="1" rowspan="1"><p></p></td></tr><tr><td colspan="1" rowspan="1"><p><code>inStock</code></p></td><td colspan="1" rowspan="1"><p>boolean</p></td><td colspan="1" rowspan="1"><p></p></td></tr><tr><td colspan="1" rowspan="1"><p><code>url</code></p></td><td colspan="1" rowspan="1"><p>https URL</p></td><td colspan="1" rowspan="1"><p>The product page.</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>addToCartUrl</code></p></td><td colspan="1" rowspan="1"><p>https URL</p></td><td colspan="1" rowspan="1"><p>Optional. A link that puts the product into the cart. The <strong>+</strong> button on the product card opens it.</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>variants</code></p></td><td colspan="1" rowspan="1"><p>array</p></td><td colspan="1" rowspan="1"><p>Optional: <code>id</code>, <code>title</code>, <code>price</code>, <code>inStock</code>, <code>stock</code>.</p></td></tr></tbody></table>

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

<table style="min-width: 75px;"><colgroup><col style="min-width: 25px;"><col style="min-width: 25px;"><col style="min-width: 25px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Field</p></th><th colspan="1" rowspan="1"><p>Type</p></th><th colspan="1" rowspan="1"><p></p></th></tr><tr><td colspan="1" rowspan="1"><p><code>number</code></p></td><td colspan="1" rowspan="1"><p>string or number</p></td><td colspan="1" rowspan="1"><p>Required. What the customer knows ("1042").</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>id</code></p></td><td colspan="1" rowspan="1"><p>string</p></td><td colspan="1" rowspan="1"><p>Your internal id. Defaults to <code>number</code>.</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>email</code></p></td><td colspan="1" rowspan="1"><p>string</p></td><td colspan="1" rowspan="1"><p>The order's email. Required for lookups by number and email.</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>customerId</code></p></td><td colspan="1" rowspan="1"><p>string</p></td><td colspan="1" rowspan="1"><p>The customer's <code>id</code> from <code>/customers</code>.</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>status</code></p></td><td colspan="1" rowspan="1"><p>string</p></td><td colspan="1" rowspan="1"><p>Required. One of <code>pending_payment</code>, <code>processing</code>, <code>on_hold</code>, <code>partially_shipped</code>, <code>shipped</code>, <code>out_for_delivery</code>, <code>delivered</code>, <code>cancelled</code>, <code>refunded</code>.</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>createdAt</code></p></td><td colspan="1" rowspan="1"><p>ISO 8601 date</p></td><td colspan="1" rowspan="1"><p>Required.</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>items</code></p></td><td colspan="1" rowspan="1"><p>array</p></td><td colspan="1" rowspan="1"><p>Required (may be empty). Each item: <code>id</code> and <code>title</code> (required), <code>quantity</code> (required, at least 1), plus <code>variant</code>, <code>image</code>, <code>price</code>, <code>productId</code> and <code>variantId</code>. The item <code>id</code> is what a return refers to.</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>total</code></p></td><td colspan="1" rowspan="1"><p><a target="_blank" rel="noopener noreferrer nofollow" class="linktype-link link" data-linktype="link" href="#money">Money</a></p></td><td colspan="1" rowspan="1"><p>Required. <code>subtotal</code>, <code>shipping</code> and <code>discount</code> are optional.</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>fulfillments</code></p></td><td colspan="1" rowspan="1"><p>array</p></td><td colspan="1" rowspan="1"><p>Shipments: <code>carrier</code>, <code>trackingNumber</code>, <code>trackingUrl</code>, <code>shippedAt</code>, <code>estimatedDeliveryAt</code>, <code>deliveredAt</code>, <code>itemIds</code>.</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>statusUrl</code></p></td><td colspan="1" rowspan="1"><p>https URL</p></td><td colspan="1" rowspan="1"><p>Your order status page, safe to give the customer.</p></td></tr></tbody></table>

### Customers

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

```json
{
  "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](https://gleap-developer-docs.gleap.help/en/articles/709-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
{
  "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
{
  "orderNumber": "1042",
  "email": "sam@example.com",
  "items": [{ "id": "line_1", "quantity": 1 }],
  "reason": "Doesn't fit",
  "exchange": false,
  "reference": "Gleap ticket 6703c1f0e4b0a1b2c3d4e5f6"
}
```

```json
{
  "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
{ "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**:

<table style="min-width: 50px;"><colgroup><col style="min-width: 25px;"><col style="min-width: 25px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Switch</p></th><th colspan="1" rowspan="1"><p>Default</p></th></tr><tr><td colspan="1" rowspan="1"><p>Kai looks up orders</p></td><td colspan="1" rowspan="1"><p>On</p></td></tr><tr><td colspan="1" rowspan="1"><p>Kai recommends products</p></td><td colspan="1" rowspan="1"><p>On</p></td></tr><tr><td colspan="1" rowspan="1"><p>Kai starts returns (needs <code>POST /returns</code>)</p></td><td colspan="1" rowspan="1"><p>Off</p></td></tr><tr><td colspan="1" rowspan="1"><p>Kai shares discount codes (needs <code>GET /discounts/{code}</code>)</p></td><td colspan="1" rowspan="1"><p>Off</p></td></tr></tbody></table>

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

## Related

- [Enforce identity verification](https://docs.gleap.ai/en/articles/709-enforce-identity-verification.md)
- [Authenticated conversation files](https://docs.gleap.ai/en/articles/710-authenticated-conversation-files.md)
- [Data regions](https://docs.gleap.ai/en/articles/711-data-regions.md)
- [Connect the Gleap MCP](https://docs.gleap.ai/en/articles/712-connect-the-gleap-mcp.md)
- [Plans and AI pricing](https://docs.gleap.ai/en/articles/713-plans-and-ai-pricing.md)
