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

# Inventory & fulfilment

> Who owns the ticket count, how to fulfil agent sales, and the polling contract — for merchants with their own inventory system.

If Alyte is your **only** sales channel, skip this page — list your stock as
`availableCount` and Alyte's atomic holds make overselling impossible.

If you have your **own** inventory system (your site sells tickets too), read
on: two systems now hold a count, and the contract matters.

## The ownership contract

* **Your system is authoritative for total stock.** Alyte's `availableCount`
  on a tier is the **allocation you've released to the agent channel** — think
  of it as tickets you've consigned, not a mirror of your whole inventory.
* **Alyte is authoritative within its allocation.** Holds and settlements
  against that allocation are atomic and oversell-proof on Alyte's side; a
  settled Alyte payment is a sold ticket you must honour.
* **You reconcile the two** by writing availability back (PATCH the tier's
  `availableCount`) as your own sales consume shared stock.

The safe pattern: **allocate conservatively, write back often.** A small
dedicated allocation (even 10–20% of stock) eliminates the cross-channel race
in practice; the write-back loop keeps the allocation honest.

## Fulfilment: webhooks first, polling as the safety net

The recommended shape is both: the [payment.settled webhook](/guides/webhooks)
fulfils in real time, and a relaxed poll catches anything missed. Both carry
the same row shape.

```
GET /v1/integration/payments?since=<ISO timestamp>&limit=200
```

* Ordered **newest first by `createdAt`**; `since` returns only payments
  created strictly after that instant — track the newest `createdAt` you've
  processed and pass it back.
* **Fulfil on `status: "authorized"`** — for ticketing that's terminal: the
  seat is sold, the buyer's authority consumed, and the charge is captured to
  your PSP moments later. Do **not** fulfil `pending`, `declined`, or `error`
  rows.
* **Fulfil deterministically**: every settled row names **`tierId`** and
  **`quantity`** — issue exactly that, never infer the product from the
  amount. `buyerRef` is a stable opaque buyer id (`byr_…`, never an email);
  map it to your own customer record at buyer-session mint time if you need
  contact details.
* **Idempotency is yours**: dedupe by `intentId`. A payment you've already
  fulfilled must be a no-op.
* **Cursor discipline**: a row can read `pending` for a second or two before
  its PSP result lands. Never advance your `since` cursor past a non-terminal
  row (or re-scan a small lookback window) — otherwise a payment that turns
  `authorized` after your poll is skipped forever. Webhooks have no such race:
  they only fire once the payment is authorized.
* A sensible poll interval is 10–30s; the endpoint is cheap and reads are
  tenant-scoped.

## Demand signal: who's waiting to buy

`GET /v1/integration/merchants/:id/insights` returns agent adoption for your
account (`total`, `readyToBuy`, …) and per-tier watch queues — how many buyers
have an agent queued for a drop or waiting in line for a sold-out release
(`{ tierId, tierTitle, eventTitle, queued, fired, failed, … }`). Counts only,
never buyer identities. `queued` is the live number ("3 in line"); the other
buckets are history. It's designed to be shown on your event pages as social
proof — a queue button converts better when it says how long the line is.

## If your local stock can't cover a settled payment

That's the cross-channel race (you sold the last seats on your site between
polls). Don't oversell locally, and don't ignore the Alyte sale — the buyer
paid. Flag it for manual resolution (refund via your PSP, or reallocate),
and shrink the window by writing availability back more aggressively. The
allocation pattern above makes this case rare to impossible.
