Twelve hours is all Apple gives you to justify a refund with evidence. That clock is worthless if the transaction in front of you belongs to a customer you cannot name. appAccountToken is the UUID that supplies the name, and it can only be attached while the purchase is happening.
Picture the notification landing. Apple wants to know how much of the product this buyer actually consumed, how long they have been a customer, what they have spent overall. Reasonable questions, and your database holds every answer. The trouble is the payload in front of you offers an originalTransactionId and a product identifier, neither of which corresponds to anything in your user table. You are holding an invoice with no name on it, and the answers you need are filed under names.
That mismatch has a fix, and the fix has a deadline. appAccountToken is a UUID your system generates, hands to StoreKit during checkout, and Apple then stamps onto the resulting transaction. From that point on, anything Apple tells you about that purchase arrives pre-identified. Miss the moment, though, and the sale in question stays anonymous permanently. Below: what the field is, how to attach it, the endpoint that retroactively rescues purchases your app never saw, and the arithmetic of what an anonymous refund actually removes from your business.
Key takeaways
Attach a UUID to an App Store purchase and the transaction it creates aims straight back at one specific user in your records. Apple keeps that value on the transaction and hands it back everywhere the transaction turns up.
Format is the sole thing Apple polices, and the format is UUID. Feed it a database id, an email address, a string you glued together, and StoreKit discards it without complaint, handing you nil where appAccountToken should be.
StoreKit 2 exposes it as a single purchase option, Product.PurchaseOption.appAccountToken(_:), fed by a UUID your system generated and filed against that account beforehand.
Attach it to the opening purchase and Apple propagates that same value through every renewal, every retried billing attempt, and every upgrade along the subscription chain.
From 2025 onward, the Set App Account Token endpoint gives your server a way to tag purchases the in-app flow never saw, offer code redemptions and promoted purchases among them.
Apple's CONSUMPTION_REQUEST only becomes answerable because of this token. Lacking it, you cannot connect the refund to the very customer whose consumption you are expected to detail inside the 12-hour window.
An unidentifiable refund is an indefensible one. Money goes back on sales you held proof for, and the compute, API calls, and payouts already burned delivering them go with it.
What this field really does
Apple treats the value as meaningless, and that is the point. Whatever UUID you hand StoreKit during checkout gets written onto the transaction and preserved indefinitely, with no attempt made to interpret it, validate what it refers to, or check that it resolves to anything at all. Apple's own definition calls it "the UUID that associates the transaction with the user's account on your own service." Interpretation is your department entirely. Apple simply carries the value around and gives it back on request.
Where it gets given back is what makes the field useful. Server notifications carry it inside the signed transaction. Ask the App Store Server API for Get Transaction Info and it appears there. Every subsequent renewal in a subscription chain reproduces it, assuming the original purchase carried it. Attach the value at one moment, in other words, and it keeps resurfacing for however long that customer keeps paying you.
Pass anything but a UUID and it vanishes without warning
Developers usually discover this rule by violating it. Something gets passed that seemed reasonable, an internal account id, an email address, a couple of fields concatenated together, and StoreKit raises no objection whatsoever. No exception, no warning, no failed purchase. The transaction simply returns with appAccountToken sitting at nil, and the connection you assumed you had was never created. Apple's developer forums carry a steady supply of this exact confusion, typically phrased as "appAccountToken is missing in the transaction payload," and the explanation is nearly always a value that failed the format requirement.
That requirement is narrow: StoreKit 2 accepts an RFC 4122 UUID and nothing else. So the safe pattern is to mint the UUID on your server, persist it against the account, and make sure no other kind of value can ever reach the purchase call.
Silence is what makes this dangerous. A malformed value costs you no error and no failed sale, only the link itself, discarded quietly while everything else proceeds normally. Checking the value against UUID format before it reaches StoreKit costs one line and removes the entire failure mode.
Attaching the token during checkout
One purchase option is the whole implementation. Generate the UUID server-side at registration, or whenever a customer first arrives at checkout, write it onto their account record, then supply that identical value every time they buy.
The option signature reads Product.PurchaseOption.appAccountToken(_ token: UUID), and the call itself looks like try await product.purchase(options: [.appAccountToken(token)]). Whichever way the finished transaction reaches you, whether you verify it yourself against the App Store Server API or receive it as a server notification, that UUID is riding along inside it, which reduces customer identification to a single indexed lookup.
One token per customer, reused forever
A tempting mistake is minting a new token per transaction. Doing so quietly dismantles the benefit. Apple reproduces whatever token was on the original purchase across renewals, retried billing attempts, and upgrades within that chain, which means a durable per-account UUID keeps a customer's entire payment history connected. Rotate the value and each purchase becomes an island. Stability is the property that matters here, so pick a UUID when the account is created and never change it.
The endpoint that saves purchases your app never touched
Two purchase routes used to defeat this entirely. Redeeming an offer code happens in the App Store, not in your app. So does buying a promoted in-app purchase from a store listing. Neither route executes your purchase code, which meant there was no moment at which a token could be supplied, and those transactions stayed permanently anonymous.
That limitation ended at WWDC 2025 with the Set App Account Token endpoint. Your server issues PUT /inApps/v1/transactions/{originalTransactionId}/appAccountToken against the App Store Server API, carrying the UUID in the body, and Apple writes it onto the transaction. Coverage extends to every product type, offer code redemptions and promoted purchases very much included, and a value sent this way replaces whatever token the transaction was already carrying. Identification has therefore become something you can perform afterwards, entirely server-side, on purchases your app was never involved in.
Where you attach the token depends on how the purchase happened:
For an ordinary in-app purchase, set it in the StoreKit purchase option at the moment of buying, via Product.PurchaseOption.appAccountToken(UUID).
For an offer code redemption, set it with the Set App Account Token endpoint from your server, because there is nothing in-app to hook into and you have to attach it afterward.
For a promoted in-app purchase from the App Store, set it the same way with the Set App Account Token endpoint, since the transaction happens beyond your app.
For a subscription renewal, you do nothing. The token is inherited automatically from the original purchase.
What a broken link does to your revenue
Record-keeping tidiness is not the argument here. The argument is that Apple asks developers exactly one question during a refund, delivered as a CONSUMPTION_REQUEST, and answering it requires knowing which of your customers is being discussed.
Requests for refunds on consumables and non-renewing subscriptions trigger that notification, and Send Consumption Information is how you respond, within twelve hours. Apple expects specifics about one individual: the proportion of the product consumed, account tenure, lifetime spend, delivery status. The token appears as a field within the request itself, but its more important role is structural, because it is the only thing capable of turning that notification into a row in your own database. Strip the token out and the request becomes unanswerable regardless of how thorough your usage tracking is.
The price of replying with nothing
Two options remain once a refund arrives unidentified, and neither is good. Submit an empty consumption response and Apple reads it as minimal usage, which weights the decision toward approval, heavy users very much included. Fabricate an estimate instead and you are reporting fiction to a platform holder. Whichever route you take, sales you could have substantiated get refunded anyway, after you already paid to fulfil them.
Fulfilment cost is the part that stings, because it bears no relation to the sale price. Model API calls consumed during a generation, images produced, a video rendered and exported, a creator payout released downstream: all of that money left your accounts at delivery, and refunding the customer does nothing to recall it. Now scale the problem. One anonymous customer files repeatedly. Habitual refunders specifically target businesses that cannot identify them. The skipped token stops being a small omission at that point and starts functioning as an open tap.
Apple's question concerns one customer's real consumption, and answering requires knowing who that customer is. Without the token you forfeit sales you could have substantiated, along with the compute and API spend already committed to delivering them.
Android runs the same play under a different label
setObfuscatedAccountId performs this job on Google Play, binding an account identifier to a purchase so that chargeback review through orders.reviewrefund has an actual person to examine. The implementations diverge considerably, but the underlying discipline transfers without modification: identity gets bound during the transaction, or the dispute is lost by default later. Apple's version of that discipline is appAccountToken, and it insists on a UUID.
Three practices that keep every purchase identified
Mint one UUID per account and persist it. A single durable token per customer, created at signup or first checkout, written to their record, reused on everything they buy.
Check the value before handing it over. Verify it really is a UUID inside your purchase code, so a malformed identifier can never quietly turn into a nil token on the transaction.
Backfill anything bought outside the app. When a server notification lands for an offer code redemption or a promoted purchase carrying no token, reach for the Set App Account Token endpoint and attach the correct one.
Adopt all three and identification stops being something you attempt after the fact. It becomes a property every transaction already has when Apple hands it to you, refund requests very much included.
Refund Sensor is built directly on that assumption. We extract appAccountToken from each transaction and server notification, join it against the consumption data already recorded for that account, and file a response to Apple's request well inside the twelve-hour window using figures that genuinely describe that buyer. A refund you can name is a refund you can argue about, which is the only kind worth arguing about at all.
Where these rules are documented
Frequently asked questions
A UUID that your system owns and StoreKit merely records. You supply it during checkout, Apple writes it onto the transaction that results, and from then on it reappears wherever that purchase does: in its transaction info, in any server notification concerning it, and in each renewal further down the same chain. The practical effect is that whatever Apple tells you later, a refund request included, can be pointed at the right row in your own database.
Format, almost certainly. RFC 4122 is the standard StoreKit 2 accepts, and a value failing that test gets discarded with no error raised, leaving nil behind while the purchase completes as though nothing had gone wrong. Internal record ids, email addresses, and stitched-together strings all fail this way. The other possibility is that your app never processed the purchase at all, an offer code redemption being the usual culprit, so no code of yours ever ran to supply a token.
Since 2025, yes. Apple introduced Set App Account Token to the App Store Server API for exactly this situation. A PUT to /inApps/v1/transactions/{originalTransactionId}/appAccountToken, UUID supplied in the body, writes a token onto a transaction that already exists, or replaces one already sitting there. Every product type qualifies, and the feature was designed around purchases your app never handled, offer codes and promoted purchases in particular.
Yes, without exception. Whatever the UUID points to on your side stays entirely your business and Apple never inspects it, but the value itself has to satisfy UUID format. Fail that and StoreKit declines to keep it, leaving nil on the transaction.
Refunds trigger a CONSUMPTION_REQUEST, and Apple expects a reply inside twelve hours describing how that specific buyer used the product. Producing such a reply means converting the transaction in the notification into a customer record you can actually query, which is precisely the token's function. It also travels as a field within the request. Without it your reply is either blank or fabricated, and a blank reply generally results in the refund being granted.






