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
availableCounton 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 sending Alyte each change your own sales make, as an adjustment: “3 fewer”, “1 back”.
Adjust stock when you sell elsewhere
When your own checkout sells, refunds or releases tickets, send the change, not a new total. Alyte applies it to its own count in one step, so tickets an agent is holding at that moment stay held.- Resending is safe. If you lose the answer, send the same
requestIdanddeltaagain. Alyte applies a change once; a resend returnsreplayed: trueand the tier’s current count, which may have moved since. - Never send a new total after creating a tier. Setting
availableCountwithPATCH /v1/integration/tiers/{id}replaces Alyte’s count, and puts tickets agents are holding back on sale. - Short stock. A decrease larger than Alyte’s count is refused with
409 stock_insufficient, anddetails.availableCountsays what Alyte has. Nothing changes, and therequestIdstays free. It means agents hold or bought tickets you also sold: decide whether to take what Alyte has (send-availableCountunder a newrequestId, so agents can’t buy more) and keep the rest to send when tickets come back. - Reusing a
requestIdfor a different tier ordeltais refused with409 stock_adjustment_conflict. - Shared pools. On a tier that draws on a pool,
the change applies to the pool. An increase that would take the pool’s
available and held places above its capacity is refused with
409 pool_capacity_exceeded; raise the capacity withPATCH /v1/integration/pools/{id}first. - Queued agents waiting for the tier are woken by an increase, as by any restock.
- General-admission tiers only; reserved seating is refused with
400. It needs an API key withmerchant:write. The SDK has no method for it yet.
Fulfilment: webhooks first, polling as the safety net
The recommended shape is both: the payment.settled webhook fulfils in real time, and a relaxed poll catches anything missed. Both carry the same row shape.- Ordered newest first by
createdAt;sincereturns only payments created strictly after that instant — track the newestcreatedAtyou’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 fulfilpending,declined, orerrorrows. - Fulfil deterministically: every settled row names
tierIdandquantity— issue exactly that, never infer the product from the amount.buyerRefis 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
pendingfor a second or two before its PSP result lands. Never advance yoursincecursor past a non-terminal row (or re-scan a small lookback window) — otherwise a payment that turnsauthorizedafter 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.

