All articles
Deep dive7 min read

Attach an appAccountToken to every App Store purchase, or you cannot defend the refund

Apple sends your server a CONSUMPTION_REQUEST when a customer asks for a refund, but the transaction never says who they are. appAccountToken is the UUID that links a purchase back to your user. Set it and you can answer Apple with real data. Skip it and you are guessing.

A numbered brass key resting on a dark ledger beside a smartphone showing a purchase receipt, illustrating how an appAccountToken links an App Store purchase to a user account

Key takeaways

  • appAccountToken is a UUID you attach to an App Store purchase so the resulting transaction points back to the exact user in your own system. Apple stores it on the transaction and returns it everywhere that transaction appears.
  • The only formatting rule Apple enforces is that the value is a valid UUID. Pass anything else, an id, an email, a concatenated string, and StoreKit silently drops it and returns appAccountToken as nil.
  • In StoreKit 2 you set it with one purchase option, Product.PurchaseOption.appAccountToken(_:), using a stable UUID you generated and stored for that account.
  • Set it once on the original purchase and Apple carries the same token across every renewal, billing retry, and upgrade in the subscription chain.
  • Since 2025 the Set App Account Token endpoint lets your server attach a token to purchases made outside your app, like offer code redemptions and promoted purchases, that the in-app flow could never reach.
  • appAccountToken is what makes Apple's CONSUMPTION_REQUEST answerable. Without it you cannot map the refund to the customer whose usage you are supposed to describe within the 12-hour window.
  • A refund you cannot identify is a refund you cannot defend. You hand back money on purchases you had evidence to keep, plus the compute, API calls, and payouts you already spent delivering them.

A customer asks Apple for a refund, Apple sends your server a CONSUMPTION_REQUEST, and you have twelve hours to answer with real data about how that person used the product. Then you open the notification and realize you have no idea who they are. The transaction carries an originalTransactionId and a product id, but nothing that points at the account in your own database. That gap is exactly what appAccountToken closes, and if you did not set it at purchase time, you cannot close it after the fact for that sale.

appAccountToken is a UUID you attach to a purchase so the resulting App Store transaction carries a pointer back to the exact user in your system. Set it, and every refund question Apple ever asks about that customer arrives with their identity attached. Skip it, and you are guessing. Here is what the field is, how to set it, the new endpoint that rescues purchases made outside your app, and what the missing link actually costs when a refund lands.

What appAccountToken actually is

appAccountToken is an opaque UUID that you generate and pass to StoreKit at the moment of purchase. Apple stores it on the transaction and returns it in the transaction info for that purchase, and it stays there. In Apple's words, it is "the UUID that associates the transaction with the user's account on your own service." The only formatting rule is that it has to be a UUID. Apple does not read it, does not validate what it maps to, and does not care what it means on your side. It is a link you control.

Because it lives on the transaction, it comes back everywhere the transaction does. The signed transaction in a server notification, the App Store Server API's Get Transaction Info response, and every renewal in a subscription chain all carry the same token if you set it on the original purchase. One UUID, attached once, follows the customer's billing for the life of the relationship.

It has to be a real UUID, or it silently disappears

The one rule Apple enforces is the format. StoreKit 2 requires an RFC 4122 UUID. If you pass a concatenated string, an integer id, or an email address, StoreKit does not throw. It drops the value and the transaction comes back with appAccountToken set to nil. Developers hit this constantly, and the symptom is always the same, some variant of "appAccountToken is missing in the transaction payload" on Apple's own forums, almost always because the value passed was not a valid UUID. Generate a real UUID server-side, store it against the account, and never hand StoreKit anything else.

How to set it at purchase time

In StoreKit 2 it is a single purchase option. Generate the UUID on your server when the user signs up or first reaches checkout, store it on their account record, and pass that same value into the purchase call.

The signature is Product.PurchaseOption.appAccountToken(_ token: UUID), and a purchase looks like try await product.purchase(options: [.appAccountToken(token)]). When the transaction comes back, verified through the App Store Server API or delivered by a server notification, it carries that UUID, and your server looks the customer up in one query.

Use one stable token per account

Do not generate a fresh token for each purchase by the same user. Apple returns the token on renewals, billing retries, and upgrades in the same chain, so a stable per-account UUID gives you a clean thread from the first purchase through every future event. A token that changes per purchase breaks that thread and defeats the entire purpose. One account, one token, reused every time that account buys.

The endpoint that rescues out-of-app purchases

Until 2025 there was a hole. If a customer redeemed an offer code or bought a promoted in-app purchase straight from the App Store, your app never ran the purchase flow, so there was nowhere to set appAccountToken. Those transactions arrived anonymous and stayed that way.

WWDC 2025 closed the gap with the Set App Account Token endpoint. Your server calls PUT /inApps/v1/transactions/{originalTransactionId}/appAccountToken on the App Store Server API with the UUID in the body, and Apple sets the token on that transaction. It works for every product type, it covers offer code redemptions and promoted purchases, and the value you send overrides any token already on the transaction. You can now associate a purchase after the fact, from your server, without it ever passing through your app.

A stack of paper sales receipts on a dark desk with the customer name line left blank, one lit by a warm spotlight, illustrating an App Store purchase that arrives with no appAccountToken to identify the buyer
Purchase channelWhere you set appAccountTokenNotes
In-app purchaseStoreKit purchase option at buy timeProduct.PurchaseOption.appAccountToken(UUID)
Offer code redemptionSet App Account Token endpoint, server-sideNo in-app flow to hook, so set it afterward
Promoted in-app purchase from the App StoreSet App Account Token endpoint, server-sideThe buy happens outside your app
Subscription renewalNothing to doCarried over from the original purchase automatically

The point of the token is not tidy records. It is that Apple's one refund question for developers, the CONSUMPTION_REQUEST, is answerable only if you can find the customer it is about.

When a buyer requests a refund on a consumable or a non-renewing subscription, Apple sends your server a CONSUMPTION_REQUEST notification and gives you twelve hours to reply with Send Consumption Information. Your answer is data about that specific customer: how much of the product they consumed, their account tenure, their lifetime spend, their delivery status. appAccountToken is itself one of the fields in that request, and more to the point, it is how the notification's transaction maps to the account whose usage you are about to describe. No token, no lookup, no accurate answer.

What a blank answer actually costs

An unidentified refund forces a bad choice. You can answer the consumption request with nothing, which reads as low consumption and nudges Apple toward granting the refund, including to customers who used the product heavily. Or you can guess. Either way you are refunding purchases you had the evidence to defend, and you already paid the real cost of serving them.

That cost is not the sale price. A consumable that ran a batch of model API calls, generated images, exported a video, or triggered a creator payout spent real money the moment it was delivered. The refund returns the customer's payment. It does not return the provider invoice. Multiply one unidentifiable customer by every refund they file, and by every serial refunder who counts on you not knowing who they are, and the token you skipped becomes the most expensive line of code you never wrote.

The same idea exists on Android, under another name

Google Play solves the identical problem with setObfuscatedAccountId, which attaches an account identifier to a purchase so Google's chargeback review through orders.reviewrefund can be answered against a real user. Different store, different mechanics, the same lesson: attach identity at purchase time or you cannot defend the dispute later. On the App Store, that tool is appAccountToken, and it has to be a UUID.

Three habits that keep the token in place

  • Generate a UUID per account and store it. One stable token per customer, created when they sign up or at first checkout, saved on their record and reused for every purchase.
  • Validate before you pass it. Confirm the value is a real UUID in your purchase code, so a malformed id can never silently become a nil token on the transaction.
  • Backfill out-of-app purchases. When a server notification arrives for an offer code redemption or a promoted purchase with no token, call the Set App Account Token endpoint to attach the right one.

Do those three and every transaction Apple ever sends you, refund requests included, arrives already tied to the customer it belongs to.

This is the groundwork RefundHalt depends on. We read appAccountToken off every transaction and server notification, tie it to the usage we already track for that account, and answer Apple's consumption request inside the twelve-hour window with the customer's real numbers. The token is the thread. Set it once and your refund defense has something to hold onto.

Frequently asked questions

What is appAccountToken in the App Store?
appAccountToken is a UUID you generate and attach to a purchase through StoreKit so the resulting App Store transaction points back to a specific user account in your own system. Apple stores it on the transaction and returns it in the transaction info, server notifications, and every renewal in the same chain, which lets you connect any future event, including a refund request, to the right customer.
Why is my appAccountToken nil or missing?
Almost always because the value you passed was not a valid UUID. StoreKit 2 requires an RFC 4122 UUID and silently drops anything else, so a concatenated string, an integer id, or an email returns as a nil appAccountToken while the purchase still succeeds. The other common cause is a purchase made outside your app, like an offer code redemption, where no in-app flow ran to set the token.
Can I set appAccountToken after the purchase, for offer codes?
Yes, since 2025. The Set App Account Token endpoint on the App Store Server API lets your server attach or overwrite the token on an existing transaction by calling PUT /inApps/v1/transactions/{originalTransactionId}/appAccountToken with the UUID in the body. It works for every product type and is built for purchases made outside your app, such as offer code redemptions and promoted in-app purchases.
Does appAccountToken have to be a UUID?
Yes. The one formatting rule Apple enforces is that the value is a valid UUID. It is otherwise opaque, so it can map to whatever account key you like on your side, but if it is not a UUID StoreKit will not store it and the transaction comes back with appAccountToken set to nil.
How does appAccountToken help with refunds?
When a customer requests a refund, Apple sends a CONSUMPTION_REQUEST and gives you twelve hours to respond with data about that specific buyer. appAccountToken is how you match the notification's transaction to the account whose usage you need to report, and it is one of the fields in the consumption request itself. Without it you cannot answer with real usage, so Apple leans toward granting refunds you had the evidence to contest.
Should appAccountToken be different for each purchase?
No. Use one stable UUID per account and reuse it for every purchase that user makes. Apple carries the token across renewals and upgrades in a subscription chain, so a stable token gives you a clean link over time. A token that changes per purchase breaks that link and makes it harder to tie transactions back to the same customer.

Sources and further reading

RefundHalt

The refund autopilot for the App Store and Google Play

Keep reading

The next refund request is already on its way.

Set up RefundHalt in the time it takes to read another support email about a refund you didn't get to contest.