Callbacks

# Receive ticket events.

Configure signed webhooks or optional XRPC receivers, validate payloads, and redrive events safely.

[Read as Markdown](https://atmosphere.tickets/docs/callbacks.md) · [Agent build guide](https://atmosphere.tickets/docs/agent-start.md)

Developer beta SDK beta 0.0.0-beta.5 30 ticket lexicons Multi-app shared config roadmap

[Quickstart](https://atmosphere.tickets/docs.html#quickstart) [Read ATM docs](https://atmosphere.money/docs)

[Overview](https://atmosphere.tickets/docs.html) [Build](https://atmosphere.tickets/docs-build.html) [Callbacks](https://atmosphere.tickets/docs-callbacks.html) [Reference](https://atmosphere.tickets/docs-reference.html) [Operations](https://atmosphere.tickets/docs-operations.html)

## Webhooks

ATM delivers ticket events to private app receiver settings. Verify signatures, environment and payload before side effects; use durable recoverable deduplication. Event names use the exact punctuation shown here.

```
ticket.hold.created
ticket.hold.expired
tickets.issued
ticket.released
ticket.refunded
ticket.voided
ticket.partial-refund-review
ticket.failed-refund-review
ticket.event-canceled
ticket.checked-in
ticket.form-submitted
ticket.waitlist.joined
ticket.waitlist.offered
ticket.collaboration.invited
ticket.collaboration.accepted
ticket.collaboration.revoked
```

## Receiver options

HTTP webhooks use `Atm-Signature: t=<unix>,v1=<hex>` and `Atm-Delivery-Id`. Verify the exact raw body; the signed input is `timestamp.deliveryId.rawBody`. Optional XRPC callbacks use ATM service-auth to the app's configured receiver, with the same current envelope and delivery headers.

```
POST https://app.example/xrpc/money.atmosphere.event.receive
Authorization: Bearer <ATM service-auth JWT for this receiver>
Atm-Delivery-Id: evt_example
Atm-Event: tickets.issued
Atm-API-Version: 2026-07
Atm-Environment: test
```

Streams are not a replacement for durable fulfillment callbacks. Persist delivery processing atomically with effects and recover interrupted deliveries before acknowledging success.

## Webhook test console

Build a current-shaped callback command shell. Replace placeholder auth/signatures; sign the exact raw body and delivery id. Real delivery status and redrive live in ATM.

Eventtickets.issuedticket.checked-inticket.hold.createdticket.refunded ReceiverHTTP webhookXRPC receiver

```
curl -X POST "https://your-app.example/webhooks/atm" \
  -H "ATM-Signature: t=...,v1=..." \
  -H "Atm-Delivery-Id: evt_demo" \
  -H "Content-Type: application/json" \
  --data '{"type":"tickets.issued","environment":"test","id":"evt_demo","createdAt":"2026-10-12T12:00:00.000Z","apiVersion":"2026-07","data":{"claimId":"claim_demo","hold":{"id":"thold_demo"},"tickets":[{"id":"tix_demo"}]}}'
```

## Payload schemas

The current ATM envelope uses `id`, `type`, ISO `createdAt`, `apiVersion`, `environment` and `data`. Delivery identity is carried in `Atm-Delivery-Id`; do not invent required root `deliveryId` or `appDid` fields. Payload `$type` names live under `money.atmosphere.event.defs`.

| ticket.hold.created / expired | `data.hold` object, including its durable id. |
| --- | --- |
| tickets.issued | `data.hold`, nonempty `data.tickets`, and `paymentId` or `claimId`. Derive counts from the tickets array; `issuedCount` is not the current required contract. |
| ticket.checked-in | `data.ticket` plus `data.checkIn.id`/`checkedInAt`. |
| ticket.refunded / voided | `paymentId`, nonempty `tickets`, optional reason. |
| ticket.released | `buyerDid`, `eventId`, nonempty `tickets`; a free release has no payment row. |
| ticket.form-submitted | Ticket, type id, timestamp and answered field keys only. Full answers require authorized `listTicketFormResponses`. |
| ticket.waitlist.* | `data.waitlist`, with redacted ids/quantity and offer hold/expiry when offered. No contact email is delivered here. |

```
{
  "id": "evt_example",
  "type": "tickets.issued",
  "createdAt": "2026-10-12T12:00:00.000Z",
  "apiVersion": "2026-07",
  "environment": "test",
  "data": {
    "$type": "money.atmosphere.event.defs#ticketsIssued",
    "claimId": "claim_example",
    "hold": { "id": "thold_example", "eventId": "tevt_example" },
    "tickets": [{ "id": "tix_example", "eventId": "tevt_example", "ticketTypeId": "ttyp_free", "status": "active" }]
  }
}
```

## Event catalog

Subscribe to exact event names in ATM's app configuration. Issuance uses nested hold/ticket payloads; free release, cancellation, partial-refund review and failed-refund review are distinct lifecycle signals. A partial refund is not permission to choose arbitrary ticket ids to void.

Form events contain answered field keys, not answers. Waitlist notifications contain offer state, not buyer contact. Co-organizer collaboration is operational delegation with manual reconciliation intent, not automatic payment splitting.

The published beta.5 typed SDK does not yet enumerate every newer lifecycle event. Verify the signed generic envelope and validate its current schema for newer events until the coordinated SDK release.

[PreviousBuild](https://atmosphere.tickets/docs-build.html) [Next pageReference](https://atmosphere.tickets/docs-reference.html)
