All articles
Deep dive8 min read

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.

A woman at a home office desk browsing a large audiobook and course library on an iPad next to a laptop, the kind of large-catalog app that can send an Advanced Commerce API refund request to Apple

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 SubscriptionRevoke SubscriptionRequest Transaction Refund
PathadvancedCommerce/v1/subscription/cancel/{transactionId}advancedCommerce/v1/subscription/revoke/{transactionId}advancedCommerce/v1/transaction/requestRefund/{transactionId}
CoversAuto-renewable subscriptionsA subscription and all its itemsOne-time charges and subscription transactions
Money backNoneFULL or PRORATEDFULL, PRORATED or CUSTOM, per SKU
AccessContinues to the end of the periodEnds at revocationDateSet per item with the revoke flag
Production rate limit5 per second5 per second5 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

ErrorCodeWhat Apple says
AlreadyRefundedError4030021The transaction was already refunded
PendingRefundError4000181The transaction has pending refunds
TransactionNotRefundableError4030024The transaction is not refundable
TransactionCannotBeRefundedContactSupportError4030025The customer can contact Apple Support
RefundAmountWithoutCustomError4000155An 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.

A developer holding an iPhone over a printed transaction ledger, checking which item an Advanced Commerce API refund should cover

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

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.