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

# App Proxy API

> Reference for the two storefront endpoints: POST /apps/live-view/track (event ingest) and GET /apps/live-view/stats (live count).

The app registers a Shopify app proxy that maps the public storefront path
`/apps/live-view/*` to the app's internal proxy routes. Requests arrive from
Shopify **HMAC-signed** — the signature covers the query parameters, including
the shop domain. Nothing is trusted before signature verification.

There are exactly two endpoints:

| Endpoint | Method | Purpose | Rate limit (per shop) |
| - | - | - | - |
| `/apps/live-view/track` | POST | Ingest tracker events | 600 batches / min |
| `/apps/live-view/stats` | GET | Live viewer count for one product | 1,200 polls / min |

Rate limits use a fixed in-memory window keyed on the **signature-verified
shop domain** — never on client-controlled headers. All responses are sent
with `Cache-Control: no-store`.

## POST /apps/live-view/track

Ingests a batch of tracker events.

* Query: `shop` (the store's `myshopify.com` domain, format-validated before
  authentication).
* Body: JSON, maximum **64 KB**, of the form `{ "events": [ ... ] }` with at
  most **25 events** ingested per batch. A legacy single-event payload shape is
  also accepted for compatibility.
* Response: `{ "accepted": <n> }` — the number of events that passed
  validation and were recorded.

### Event fields

Every event carries `sid` (the anonymous session UUID) and product identity:
`pid` (numeric product id) or `handle` (product slug, lowercase, up to 120
characters). Validation is strict:

| Field | Applies to | Rule |
| - | - | - |
| `sid` | all | Must be a UUID |
| `pid` | all | Numeric, up to 25 digits — or `handle` instead |
| `handle` | all | `^[a-z0-9][a-z0-9._-]{0,119}$`; resolved against the cached catalog, unknown handles are dropped |
| `vid` | all | Variant id when known (optional) |
| `device` | all | `mobile`, `tablet`, or `desktop` |
| `qty` | cart events | Quantity delta, clamped to 1–999 per line |
| `cartToken` | cart events | Hex token (16–64 chars); the attribution key |
| `tagged`, `ref`, UTM fields | first event of a session only | Referrer host and campaign parameters, truncated to 120 chars; later events carry none |

Handle-only events resolve against the product cache per batch. Malformed
events are **dropped silently** — the rest of the batch still ingests and the
request never fails the storefront.

```json theme={null}
{
  "events": [
    { "sid": "8f6c…", "pid": "7890123", "device": "mobile" },
    { "sid": "8f6c…", "pid": "7890123", "device": "mobile" }
  ]
}
```

> [!NOTE]
>
> A view event is only recorded once per shop + session + product inside the
> configured dedupe window (default 30 minutes). Heartbeats refresh presence
> instead of creating events.

## GET /apps/live-view/stats

Returns the live count the counter block renders.

* Query: `shop` plus exactly one product identifier — `productId`
  (1–24 digits) or `handle` (slug). Anything else is a 400.
* Response:

```json theme={null}
{ "count": 3, "visible": true }
```

* `count` is the number of distinct sessions with a heartbeat inside the
  presence window (`activeWindowSeconds`, default 60 s).
* `visible` is `false` — and the widget hides itself — when tracking is
  disabled for the shop, the product cannot be resolved, the product's counter
  is individually disabled, or `count` is below the minimum-views threshold.
  The endpoint **never fabricates a count**.

## Authentication

1. **Shop format check** (pre-auth): the `shop` parameter must look like a
   valid `myshopify.com` domain, or the request is rejected with 400 before
   any signature work.
2. **Proxy signature verification**: Shopify's HMAC signature over the query
   parameters is verified by the app library. Missing or tampered signatures
   are rejected with 400/401.
3. **Installation check**: the verified shop must have an installed session in
   the app; otherwise 403.
4. **Rate limit**: per verified shop, per the table above; over-limit requests
   get 429.

## Error responses

| Status | Meaning |
| - | - |
| 400 | Invalid shop parameter, invalid stats params, malformed JSON body, or signature failure |
| 401 | Request failed proxy authentication |
| 403 | Signature valid but the shop is not installed |
| 405 | Wrong HTTP method (GET on track, POST on stats) |
| 413 | Body over the 64 KB limit |
| 429 | Per-shop rate limit exceeded |
| 500 | Unexpected failure while recording events (ingest path only) |

> [!WARNING]
>
> These endpoints are for the theme extension. Do not build custom callers
> against them: payloads are validated strictly, unsigned requests are always
> rejected, and the wire format may change. Integration points for merchants
> are the theme blocks and the [admin app](https://topten.mintlify.site/guides/dashboard.md).


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