Build your first app
A complete ATM integration walkthrough from app registration to fulfillment.
Compatible with the closed-beta ATM app APIs and versioned ATM event headers. Check atm-api-version on every webhook or XRPC receiver event.
Before you start
ATM integrations should be server-led. Your client can show product pages, ticket pickers, and support buttons, but your server should create checkout envelopes, mint service-auth, verify events, and fulfill app orders.
You need
An app DID, an ATM app role, and a server that can call ATM XRPC methods.
Keep private
Webhook secrets, checkout envelopes, app order ids, customer details, and service-auth signing material.
Use test first
Configure the app test environment before requesting live payment access.
Fulfill later
Use ATM events or status, not browser redirects, as payment truth.
1. Create the app profile
Sign into ATM with the DID that represents your app, then register it as an app during the beta. The app still has a normal ATM profile for name, icon, and description; app-specific settings live in the app dashboard.
- 01
Sign in
Use the app account DID.
- 02
Register app
ATM adds the app role without deleting payer or creator roles.
- 03
Choose payment setup
Pick app-fee payouts only, direct payments, or both.
- 04
Choose modules
Enable payments, products, subscriptions, tickets, or future modules in test mode.
- 05
Copy secrets
Store test webhook secrets and service-auth config server-side.
2. Configure test mode
Test and live environments are separate. Start with test webhooks, test modules, and test fees. ATM queues events even before a webhook URL exists, so you can register early and add infrastructure later.
| Webhook URL | Where ATM sends signed HTTP events for this environment. |
|---|---|
| XRPC receiver | Optional AT Protocol-native event receiver for apps that already host XRPC. |
| Modules | The app features enabled in this environment. |
| Fee share | The app fee and optional ATM support share for app-originated checkouts. |
3. Check recipient payability
Before showing a live payment button, ask ATM whether the recipient can receive payments. This avoids sending buyers into a checkout that cannot complete.
GET /xrpc/money.atmosphere.actor.getPayoutStatus?actor=did:plc:creator
Authorization: Bearer <app service-auth jwt>4. Request creator approval
If the checkout is for a creator or organizer, ask them to approve this app before creating checkout. ATM checks the approved payment types, app fee cap, environment, and public-record policy at checkout time.
const approval = await atm.requestRecipientApproval({
recipientDid: "did:plc:creator",
environment: "test",
paymentTypes: ["shop"],
feeShareBps: 300,
requestReason: "Enable Example App checkout"
});
if (approval.status !== "approved") {
return Response.redirect(approval.dashboardUrl);
}5. Create checkout
Create the app order first, then create the ATM checkout envelope. The envelope can include an app order id and safe public strongRefs, but customer details and fulfillment fields stay private.
const checkout = await atm.initiatePayment({
environment: "test",
recipient: "did:plc:creator",
paymentType: "shop",
amount: 2500,
currency: "usd",
listing: productStrongRef,
metadata: { appOrderId: order.id },
returnUrl: "https://app.example/orders/" + order.id,
cancelUrl: "https://app.example/products/" + product.slug
});
return Response.redirect(checkout.url);6. Handle return
The buyer may return before processor webhooks and app events finish moving. Show a processing state, poll ATM status, and update the app order when ATM confirms payment.
GET /xrpc/network.attested.payment.status?token=atm_checkout_...
Authorization: Bearer <app service-auth jwt>7. Fulfill from events
Fulfillment should be event-driven. Verify signatures, deduplicate delivery ids, and make the final app-side mutation idempotent.
- 01
payment.completed
ATM has confirmed payment settlement.
- 02
Verify
Check webhook signature or XRPC service-auth.
- 03
Deduplicate
Store delivery id and payment id before side effects.
- 04
Fulfill
Mark the order paid, issue content, or notify the buyer.
8. Move to live
Live mode should use live webhook secrets, live payment rails, and a live app review. Keep test traffic isolated so beta and regression checks never mix into production reporting.
- Test guest and signed-in buyer checkout.
- Verify webhook signature handling with the raw body.
- Redrive a failed delivery from the app dashboard.
- Confirm refund and cancellation behavior.
- Confirm support contact and privacy/terms links.
- Switch app modules and webhooks to live intentionally.