# App Integration

Keep community event/RSVP records as the public event graph. The event app owns
pages, discovery, ticket selection, organizer UX, attendee display and optional
scanner UI. Its trusted backend maps signed-in sessions and exact user
assertions to ATM Tickets calls. ATM owns checkout and payment settlement;
Tickets owns transactional capacity and issued-ticket truth.

## Setup

Register the app in ATM, enable Tickets for the selected test environment,
configure app service-auth and verified HTTP/XRPC callbacks, and obtain the
organizer's selling approval. Event/type creation needs organizer assertion
unless the app is itself the organizer. Later mutations require current event
control, not app creation provenance.

Create `createTicketEvent`, `createTicketType`, optional `setEventCapacity` and
`setTicketTypeForm` configuration. Paid types require a matching active
organizer-owned ticket Product/Price graph. Free types can omit Product.
Consult [LEXICONS.md](LEXICONS.md) for every method's verb and exact body.
`listEventWaitlist`, `listTicketFormResponses` and `listCollaborations` are POST,
while availability and private ticket lists are GET. `listPublicConfig` is a
separate unauthenticated AppView GET.

## Buyer flow

```text
GET getTicketAvailability -> ticketTypes
POST createTicketHold -> holdId, statusToken, token, url, expiresAt
redirect to url
verified tickets.issued or GET listBuyerTickets -> display issued tickets
```

Use exactly one of `eventId` or `eventUri`, and
`items: [{ ticketTypeId, quantity }]`. Store stable operation keys and status
capabilities server-side. Paid buyers supply both `buyerDid` and assertion or
neither. Fixed asserted identity is the default; explicit switchable payer
flows must reconcile `CheckoutPayerChanged`.

Fresh free claims require both buyer fields, `customerEmail` and the item array,
without checkout. They should use a stable idempotency key for retries;
the claim's wire key is optional. `releaseFreeTicket` requires its own
buyer assertion and stable release key. Waitlist offers reserve capacity but
never auto-charge. Hidden-type access codes and price coupons remain private.

`listBuyerTickets` uses `buyer` (DID/handle) or `paymentId`, exactly one;
`listOrganizerTickets` uses `organizer`. Published SDK beta.5 supports current
item arrays but still types/sends older list selector names. Use direct
authenticated XRPC for the new selectors and newer fields/methods until the
coordinated SDK release; do not assume ATM's unreleased SDK source is published.

Beta.5 types paid catalog refs as `productRef`/`priceRef` and sends those names
unchanged. ATM's wire field is `product` (omit `defaultPrice` when supplying it).
Create and update paid types with authenticated raw XRPC until the coordinated
SDK release; the SDK's old ref fields do not satisfy paid type setup.

## Receiver and scanner flow

Verify raw callback bodies/signatures and the target environment. Use durable
atomic deduplication and processing state; a logged delivery id alone is not
fulfillment. Callbacks are at-least-once, including redrive.

QR/wallet passes carry only opaque scan URLs/tokens. App scanner backends use
app service-auth for `verifyTicket`/`checkInTicket`; check-in is idempotent per
ticket/list and has no current wire `idempotencyKey`. The staff PWA has its own
OAuth session and grants. The machine bearer pilot is separate; see
[SCANNER_API.md](SCANNER_API.md).

Run the [public starter](https://atmosphere.tickets/docs/tickets-node-app.zip)
using the [build guide](https://atmosphere.tickets/docs/agent-start.md), then the acceptance checks in
[LAUNCH_CHECKLIST.md](LAUNCH_CHECKLIST.md). Local mock checks do not establish
live-sales approval, deployed migrations, credential cutover or publication.
