Skip to main content
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 sending Alyte each change your own sales make, as an adjustment: “3 fewer”, “1 back”.
The safe pattern: allocate conservatively, adjust promptly. A small dedicated allocation (even 10–20% of stock) eliminates the cross-channel race in practice; sending each change as it happens keeps the allocation honest.

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 requestId and delta again. Alyte applies a change once; a resend returns replayed: true and the tier’s current count, which may have moved since.
  • Never send a new total after creating a tier. Setting availableCount with PATCH /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, and details.availableCount says what Alyte has. Nothing changes, and the requestId stays free. It means agents hold or bought tickets you also sold: decide whether to take what Alyte has (send -availableCount under a new requestId, so agents can’t buy more) and keep the rest to send when tickets come back.
  • Reusing a requestId for a different tier or delta is refused with 409 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 with PATCH /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 with merchant: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; 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 sending each of your own sales as an adjustment as soon as it happens. The allocation pattern above makes this case rare to impossible.