An Advanced Commerce API refund is the one way an iOS developer can start a refund, and Apple still makes the final call
Most iOS developers can't refund a customer. Apps on Apple's Advanced Commerce API can request one, revoke a subscription, or send a custom amount back. Here's how each endpoint works, what Apple can still decline, and where the money goes wrong.

Key takeaways
- An Apple engineer said in June 2026 that a developer can't directly issue a refund for an ordinary App Store purchase. The developer's only input is the consumption data and refundPreference sent in reply to a CONSUMPTION_REQUEST.
- Apps approved for Apple's Advanced Commerce API get a Request Transaction Refund endpoint that asks the App Store to refund a one-time charge or subscription transaction, plus Revoke Subscription and Cancel a Subscription endpoints.
- To call the Advanced Commerce API refund, revoke and cancel endpoints, the developer's Account Holder must sign the Advanced Commerce API Addendum, and Apple says the call returns an error if the latest version hasn't been accepted.
- The Request Transaction Refund endpoint takes a refundType of FULL, PRORATED or CUSTOM, and a refundAmount in milliunits of the currency is allowed only with CUSTOM. Apple says the amount must include tax in every region except Canada and the United States.
- Apple's refundRiskingPreference field defaults to true, which lets the App Store send a CONSUMPTION_REQUEST about the developer's own refund request. Apple asks for a response within 12 hours.
- The Advanced Commerce API refund, revoke and cancel endpoints are limited to 5 requests per second per app in production, and sandbox gets 10 percent of that.
- After a successful Revoke Subscription call, Apple sends a REFUND notification and tells developers to end service as of its revocationDate, not before the notification arrives.
Ordinary iOS developers can't refund a customer. In June 2026 an App Store Commerce Engineer answered a developer forum question with one line: at this time it is not possible as a developer to directly issue a refund. There is one exception, and it sits inside the Advanced Commerce API. Apps Apple approves for it can send an Advanced Commerce API refund request, revoke a subscription with money back, or ask for a custom amount. Apple still decides whether the money moves.
What the Advanced Commerce API is and who gets it
Apple released the Advanced Commerce API on January 23, 2025. It handles Apple In-App Purchases for exceptionally large catalogs of custom one-time purchases, subscriptions, and subscriptions with optional add-ons. You don't create every product in App Store Connect. You define SKUs on your own server and send them to Apple at purchase time.
Access is granted app by app. You fill in a request form, and Apple's eligibility page names three business models that must be core to the app:
- Exceptionally large catalogs of one-time purchases, such as audiobooks, classes, courses or mini apps.
- Exceptionally large catalogs of subscription content, where each creator's offering is its own subscription.
- A subscription with optional add-ons that renew together as one purchase, such as premium channels or sports.
Apple also lets apps apply when another program needs the API. The Mini Apps Partner Program, which pays members 85% of qualifying In-App Purchase sales in hosted mini apps, requires it.
The refund endpoints arrived later, and they need a signature
Version 1.1, dated March 24, 2025, added Request Transaction Refund and Revoke Subscription. Apple's program page says apps offering subscription content may also be eligible to use the Cancel, Revoke and Refund APIs to handle cancellations, validate content after purchase and manage their catalogs. Each endpoint page adds the same condition: your Account Holder must sign the Advanced Commerce API Addendum, and if the latest version isn't accepted, the call returns an error. The Addendum, not the docs, lists the use cases each endpoint is meant for.
The three endpoints, side by side
| Cancel a Subscription | Revoke Subscription | Request Transaction Refund | |
|---|---|---|---|
| Path | advancedCommerce/v1/subscription/cancel/{transactionId} | advancedCommerce/v1/subscription/revoke/{transactionId} | advancedCommerce/v1/transaction/requestRefund/{transactionId} |
| Covers | Auto-renewable subscriptions | A subscription and all its items | One-time charges and subscription transactions |
| Money back | None | FULL or PRORATED | FULL, PRORATED or CUSTOM, per SKU |
| Access | Continues to the end of the period | Ends at revocationDate | Set per item with the revoke flag |
| Production rate limit | 5 per second | 5 per second | 5 per second |
Cancel a Subscription stops the next charge
Cancel turns auto-renew off. Apple says the customer keeps access until the end of the current period. Nothing is refunded. It's the right call when a customer wants out and has already used the time they paid for.
Revoke Subscription ends everything now
Revoke sets auto-renew to false and revokes the subscription with a full or prorated refund. Its body takes refundReason, refundRiskingPreference, refundType and requestInfo. Apple then sends a REFUND notification to your App Store Server Notifications V2 endpoint. Apple's instruction is specific: turn off service as of the revocationDate in that notification, and don't turn it off before the notification arrives.
Request Transaction Refund works item by item
This is the precise tool. You post a list of items, and each one names a SKU, a refundReason, a refundType and a revoke flag. With CUSTOM you also send refundAmount in milliunits of the currency, so 4990 means 4.99. Send an amount with FULL or PRORATED and Apple rejects it with error 4000155. The response carries a signed transaction and, for subscriptions, signed renewal info.
Apple accepts these refundReason values: UNINTENDED_PURCHASE, FULFILLMENT_ISSUE, UNSATISFIED_WITH_PURCHASE, LEGAL, OTHER and MODIFY_ITEMS_REFUND. A seventh, SIMULATE_REFUND_DECLINE, is for sandbox and TestFlight testing only, and it exists because the App Store can say no.
Apple can still decline your own refund request
The endpoint's name is honest. It's a request. Two fields show where the decision really sits.
refundRiskingPreference invites a consumption check
Apple describes refundRiskingPreference as whether the App Store asks you for consumption data to inform the refund decision. It defaults to true. When it's true, you receive a CONSUMPTION_REQUEST for your own refund request and answer through Send Consumption Information. Apple asks developers to respond within 12 hours of receiving a CONSUMPTION_REQUEST. Set it to false and that notification doesn't come.
The errors that tell you no
| Error | Code | What Apple says |
|---|---|---|
| AlreadyRefundedError | 4030021 | The transaction was already refunded |
| PendingRefundError | 4000181 | The transaction has pending refunds |
| TransactionNotRefundableError | 4030024 | The transaction is not refundable |
| TransactionCannotBeRefundedContactSupportError | 4030025 | The customer can contact Apple Support |
| RefundAmountWithoutCustomError | 4000155 | An amount was sent without CUSTOM |
PendingRefundError is the one to plan for. A likely cause is a customer who already asked Apple through Report a Problem before your support team acted. Apple's eligibility rules require these apps to offer an in-app path to a refund anyway, either a Report a Problem link or the beginRefundRequest API, so both doors stay open at once.

What an Advanced Commerce API refund costs you in money
Refunding on your own initiative feels cheaper than fighting a dispute. It's still money out, and the API makes some mistakes easy to repeat at scale.
Tax inside the custom amount
Apple says a requested refundAmount must include tax for all regions except Canada and the United States. A team that computes a CUSTOM refund from its pre-tax SKU price will under-refund customers in every other storefront, then pay support time to fix each one.
Costs that don't come back
- Payouts: in a creator catalog or a mini app host, the creator's share is often paid on the sale. A refund to the customer doesn't recover it.
- Content licensing: per-title fees owed to a rights holder for an audiobook or course can be triggered by access, depending on your contract, and a refund doesn't reverse them.
- Compute: streaming, transcoding and any server work done for the purchase are already on your bill.
- API calls: third-party services billed per request, from speech to model inference, stay billed after the refund.
Full when you meant one item
Revoke Subscription refunds and ends the whole subscription, add-ons included. If the customer only disputes one add-on, Request Transaction Refund lets you refund that SKU alone, with revoke set for just that item. Calling Revoke Subscription over a one-item complaint gives back the base plan revenue too.
A build checklist before you switch these on
- Confirm your Account Holder has accepted the current Advanced Commerce API Addendum, and alert on the error that comes back when it lapses.
- Generate a new requestReferenceId UUID for every request, and reuse the same one only to retry a timed-out call.
- Queue refund calls. The limit is 5 per second per app, and a 429 carries a Retry-After header in UNIX milliseconds.
- Test declines in sandbox with SIMULATE_REFUND_DECLINE, and remember sandbox limits are 10 percent of production.
- Decide refundRiskingPreference per case, and keep a consumption responder ready for the 12-hour window when it stays true.
- End access only when the REFUND notification arrives, as of its revocationDate.
- Log the reason, amount and SKU for every request, so your own refund rate is visible before Apple's is.
Frequently asked questions
- Can an iOS developer refund a customer directly?
- Not for an ordinary App Store purchase. An Apple engineer said in June 2026 that it isn't possible for a developer to directly issue a refund. Apps approved for the Advanced Commerce API, with the Addendum signed, can request one through the Request Transaction Refund endpoint, and Apple still decides.
- What is the difference between Cancel and Revoke in the Advanced Commerce API?
- Cancel turns off auto-renewal and the customer keeps access until the period ends, with no refund. Revoke ends the subscription and all its items immediately and refunds the full or prorated amount. Apple sends a REFUND notification after a revoke.
- Can I refund a custom amount with the Advanced Commerce API?
- Yes. Set refundType to CUSTOM and pass refundAmount in milliunits of the currency. Apple says the amount must include tax for all regions except Canada and the United States, and sending an amount with FULL or PRORATED returns error 4000155.
- Will I get a CONSUMPTION_REQUEST for a refund I requested myself?
- You can. refundRiskingPreference defaults to true, which lets the App Store ask you for consumption data through a CONSUMPTION_REQUEST, and Apple asks for a reply within 12 hours. Set it to false and Apple says that notification won't be sent.
- Why did my Advanced Commerce API refund request fail?
- Check the error code. 4030021 means the transaction was already refunded, 4000181 means a refund is already pending, 4030024 means it isn't refundable, and 4030025 means the customer should contact Apple Support. An unsigned or outdated Addendum also returns an error.
Sources and further reading
- Apple Developer: Advanced Commerce API eligibility and requirements
- Advanced Commerce API: Request Transaction Refund
- Advanced Commerce API: RequestRefundItem
- Advanced Commerce API: refundRiskingPreference
- Advanced Commerce API: Revoke Subscription
- Advanced Commerce API: Rate limits
- Advanced Commerce API changelog
- App Store Server API: Send Consumption Information
- Apple Developer Forums: Refund option in App Store Server API
- Apple Developer: Mini Apps Partner Program
RefundHalt
The refund autopilot for the App Store and Google Play
Keep reading
Excessive refund requests can cost you more than the refunds, because both app stores treat them as a quality signal
Apple's Guideline 5.6.4 names excessive refund requests as a sign an app isn't meeting quality standards, and Google's Distribution Agreement lets it remove new developers' apps on refund rates. Here's what each store says, what removal costs, and how to stay clear.
Verify App Store server notifications before you act on a refund, because anyone can post to your webhook URL
A refund notification is just an HTTPS request until you check its signature. Here's how to verify App Store server notifications and Google Play RTDN messages, what each store signs, and what a forged or wrongly rejected refund costs you.