Docs

Webhook events

Canonical ATM event shapes for app fulfillment, subscriptions, ticketing, and operational redrive.

Closed beta@atmosphere-money/app-nodeSDK beta: 0.0.0-beta.3ATM API beta: 2026-0672 lexicons

Compatible with the closed-beta ATM app APIs and versioned ATM event headers. Check atm-api-version on every webhook or XRPC receiver event.

Event envelope

Every app event carries delivery metadata, an environment, a type, and a typed data payload. HTTP webhooks are signed with the app environment secret. XRPC receiver callbacks are authenticated by service-auth in the opposite direction.

json
{
  "id": "evt_...",
  "deliveryId": "del_...",
  "environment": "test",
  "type": "payment.completed",
  "appDid": "did:plc:app",
  "createdAt": "2026-06-05T00:00:00.000Z",
  "data": {}
}

Event catalog

This catalog is generated from the same event definition list used by the app dashboard event selector. If an event appears here, apps can subscribe to it in their test or live environment when the matching module is enabled.

EventModulePurposeExample
app.webhook.testcoreTest eventA manual ping from the app dashboard to verify signatures and delivery.{"message":"ATM webhook test event"}
payment.completedpaymentsPayment completedA one-time, product, commission, ticket, or subscription invoice payment settled.{"payment":{"id":"pmt_...","status":"completed","amount":500,"currency":"usd"}}
payment.failedpaymentsPayment failedA checkout or invoice payment failed before settlement.{"payment":{"id":"pmt_...","status":"failed"},"reason":"payment_failed"}
payment.refundedpaymentsPayment refundedA payment was fully or partially refunded.{"payment":{"id":"pmt_...","status":"refunded"},"amount":500,"reason":"refunded","distribution":{"attempted":1,"reversed":1,"skipped":0,"failed":0}}
payment.disputedpaymentsPayment disputedThe payer opened a dispute or chargeback for an app-originated payment.{"payment":{"id":"pmt_...","status":"disputed"},"status":"needs_response","distribution":{"attempted":1,"reversed":1,"skipped":0,"failed":0}}
subscription.invoice-paidsubscriptionsSubscription invoice paidA recurring subscription invoice settled.{"payment":{"id":"pmt_..."},"invoice":{"id":"in_...","firstInvoice":false},"invoiceId":"in_..."}
subscription.updatedsubscriptionsSubscription updatedA subscription amount or status changed through ATM.{"subscription":{"id":"pmt_...","status":"active"},"payment":{"id":"pmt_new..."},"priorAmount":500,"amount":700,"currency":"usd","updatedAt":"2026-05-27T00:00:00.000Z"}
subscription.canceledsubscriptionsSubscription canceledA subscription was canceled through ATM or processor state.{"subscription":{"id":"pmt_...","status":"canceled"},"payment":{"id":"pmt_..."},"canceledAt":"2026-05-27T00:00:00.000Z"}
subscription.payment-failedsubscriptionsSubscription payment failedA recurring renewal invoice failed to collect (card declined/expired, or needs SCA/3DS re-authentication — reason: authentication-required, with hostedInvoiceUrl set); the subscription is now past due. Charge/invoice-scoped — the subscription is NOT canceled.{"subscription":{"id":"pmt_...","status":"past_due"},"payment":{"id":"pmt_..."},"invoiceId":"in_...","reason":"authentication-required","hostedInvoiceUrl":"https://invoice.stripe.com/i/...","failedAt":"2026-05-27T00:00:00.000Z"}
subscription.recoveredsubscriptionsSubscription recoveredA past-due subscription recovered — a renewal retry succeeded or Stripe reported the subscription active again. Apps should re-enable entitlements.{"subscription":{"id":"pmt_...","status":"active"},"payment":{"id":"pmt_..."},"recoveredAt":"2026-05-27T00:00:00.000Z"}
pledge.createdpledgesPledge createdA payer saved a card pledging to subscribe when the recipient enables payments. No charge was made.{"pledge":{"id":"plg_...","recipientDid":"did:plc:...","amount":500,"currency":"usd","interval":"month","status":"active","autoConvert":true,"createdAt":"2026-07-05T00:00:00.000Z"}}
pledge.canceledpledgesPledge ended without convertingThe pledge was canceled by the payer/app/creator, superseded by a direct subscription, or expired. Read pledge.status for which.{"pledge":{"id":"plg_...","status":"canceled","canceledBy":"payer"}}
pledge.convertedpledgesPledge convertedThe recipient became payable and the pledge converted into a live subscription; the saved card was charged. Settlement still emits payment.completed / subscription.invoice-paid for the first invoice.{"pledge":{"id":"plg_...","status":"converted"},"paymentId":"pmt_...","subscriptionId":"sub_..."}
pledge.conversion-failedpledgesPledge conversion failedConverting the pledge failed (card declined or authentication required). When willRetry is false the pledge is terminal and the app owns re-engagement.{"pledge":{"id":"plg_...","status":"failed"},"reasonCode":"card_declined","willRetry":false}
payer.record.requestedpaymentsPayer record requestedATM is asking a delegated app to write the canonical payer record.{"paymentId":"pmt_...","payerDid":"did:plc:...","recipientDid":"did:plc:...","collection":"network.attested.payment.oneTime","canonicalRecord":{},"expectedCid":"bafy...","expiresAt":"2026-05-27T00:00:00.000Z"}
creator.proof.requestedpaymentsCreator proof requestedATM is asking a delegated app to write the canonical creator proof.{"paymentId":"pmt_...","creatorDid":"did:plc:...","attestationCid":"bafy...","proofRecord":{},"expiresAt":"2026-05-27T00:00:00.000Z"}
attestation.updatedpaymentsAttestation updatedOne of the payer, broker, or creator proof slots changed.{"paymentId":"pmt_...","attestations":{"broker":{"status":"saved"}}}
product.updatedproductsProduct updatedShared ATM catalog fields changed for a product fulfilled by this app.{"product":{"uri":"at://did:plc:.../money.atmosphere.product/..."}}
product.archivedproductsProduct archivedA product was removed from sale while keeping payment history intact.{"product":{"active":false,"archivedAt":"2026-05-27T00:00:00.000Z"}}
product.deletedproductsProduct deletedA hard delete/tombstone path ran for a public product record.{"product":{"uri":"at://did:plc:.../money.atmosphere.product/..."}}
ticket.hold.createdticketsTicket hold createdA buyer reserved ticket capacity before checkout.{"hold":{"id":"th_...","eventId":"evt_..."}}
ticket.hold.expiredticketsTicket hold expiredA ticket hold expired or was released before payment.{"hold":{"id":"th_...","status":"expired","reason":"expired"}}
tickets.issuedticketsTickets issuedA paid checkout issued one or more tickets.{"paymentId":"pmt_...","tickets":[{"id":"tkt_..."}]}
ticket.voidedticketsTicket voidedA ticket was voided after a dispute, reversal, or admin action.{"paymentId":"pmt_...","reason":"voided"}
ticket.refundedticketsTicket refundedA refund changed the ticket state.{"paymentId":"pmt_...","reason":"refunded"}
ticket.checked-inticketsTicket checked inA ticket was verified and checked in.{"ticket":{"id":"tkt_...","status":"active"},"checkIn":{"id":"tchk_...","checkedInAt":"2026-05-27T00:00:00.000Z"}}
ticket.form-submittedticketsTicket form submittedAn attendee filled the per-tier questionnaire through the anonymous ticket page. Carries the answered field keys only; fetch values via tickets.atmosphere.listTicketFormResponses if entitled.{"ticket":{"id":"tkt_...","status":"active"},"tierId":"tier_...","submittedAt":"2026-06-20T00:00:00.000Z","answeredFieldKeys":["meal-choice","tshirt-size"]}
ticket.waitlist.joinedticketsTicket waitlist joinedA buyer joined the waitlist for a sold-out capacity group. No capacity is taken; carries ids + requested quantity only.{"waitlist":{"id":"twl_...","eventId":"evt_...","capacityGroupId":"tcap_...","quantity":1}}
ticket.waitlist.offeredticketsTicket waitlist offeredCapacity reopened and ATM auto-OFFERed a held seat to the next FIFO waitlister. ATM never auto-charges — email the buyer to claim before offerExpiresAt.{"waitlist":{"id":"twl_...","eventId":"evt_...","capacityGroupId":"tcap_...","quantity":1,"offerHoldId":"thold_...","offerExpiresAt":"2026-06-20T00:30:00.000Z"}}
customer.segment.message-requestedcoreCustomer segment message requestedThe app asked ATM (via money.atmosphere.segments.requestMessage) to fan a marketing message out to a computed customer cohort. ATM never sends marketing email — this event tells the app to do its own send.{"segmentKey":"at-risk-churn","messageType":"winback_v1","memberCount":12,"requestedAt":"2026-06-19T00:00:00.000Z"}
recipient.authorization.updatedcoreRecipient authorization updatedA creator/recipient approved, blocked, or revoked this app's authorization to accept payments on their behalf. status=approved means the app may originate payments for recipientDid; blocked/revoked means it must stop. Delivered as a core event so an app is always told when its authorization changes.{"recipientDid":"did:plc:...","status":"approved","approvedPaymentTypes":["one-time","subscription"],"approvedFeeShareBps":500,"updatedAt":"2026-06-23T00:00:00.000Z"}

Payment events

payment.completed

A one-time payment or first subscription invoice has settled and can be fulfilled.

json
{
  "paymentId": "pay_...",
  "amount": 500,
  "currency": "usd",
  "paymentType": "shop",
  "recipientDid": "did:plc:creator",
  "payerDid": "did:plc:buyer",
  "listing": { "uri": "at://...", "cid": "bafy..." }
}

payment.refunded

A payment was refunded. Apps should reverse fulfillment when appropriate.

json
{
  "paymentId": "pay_...",
  "refundId": "re_...",
  "amountRefunded": 500,
  "currency": "usd"
}

payment.disputed

A payment has a dispute or chargeback state that may require fulfillment review.

json
{
  "paymentId": "pay_...",
  "disputeId": "dp_...",
  "status": "needs_response"
}

Subscription events

subscription.updated

A subscription amount, status, or payment method state changed.

json
{
  "subscriptionId": "sub_...",
  "status": "active",
  "amount": 1000,
  "previousAmount": 500,
  "currency": "usd"
}

subscription.canceled

A subscription was canceled through ATM or the underlying billing rail.

json
{
  "subscriptionId": "sub_...",
  "status": "canceled",
  "canceledAt": "2026-06-05T00:00:00.000Z"
}

Catalog events

product.updated

Shared public catalog fields changed.

json
{
  "productUri": "at://did:plc:creator/money.atmosphere.product/abc",
  "productCid": "bafy...",
  "changedFields": ["title", "price"]
}

product.archived

A product was removed from sale but kept for historical receipts and payments.

json
{
  "productUri": "at://did:plc:creator/money.atmosphere.product/abc",
  "archivedAt": "2026-06-05T00:00:00.000Z"
}

Ticket events

ticket.hold.created

A ticket hold reserved capacity before checkout.

json
{
  "holdId": "hold_...",
  "eventUri": "at://...",
  "expiresAt": "2026-06-05T00:10:00.000Z"
}

tickets.issued

Ticket issuance completed after ATM confirmed payment or a free claim.

json
{
  "paymentId": "pay_...",
  "eventUri": "at://...",
  "issuedCount": 2
}

ticket.checked-in

A ticket token was verified and checked in.

json
{
  "ticketId": "ticket_...",
  "eventUri": "at://...",
  "checkedInAt": "2026-06-05T00:00:00.000Z"
}

Verification

  • Verify webhook signatures against the raw body.
  • Deduplicate by delivery id before side effects.
  • Use the environment field to separate test from live traffic.
  • Redrive events from the app dashboard if delivery fails.
Webhook events - Atmosphere Money Docs