Apple refund requests: CONSUMPTION_REQUEST and how to answer it
When a customer asks Apple for a refund on an in-app purchase, Apple sends your server a CONSUMPTION_REQUEST notification. You have 12 hours to answer by calling the App Store Server API's Send Consumption Information endpoint, but only if the customer agreed to share their data. Apple weighs your answer, including your refund preference, with other factors, then decides. It tells you the result with a REFUND or REFUND_DECLINED notification.
This post follows Apple's Send Consumption Information documentation, read on October 1, 2026.
The short answer#
- Trigger: a customer starts a refund request with Apple. Apple sends the
CONSUMPTION_REQUESTnotification type to your App Store Server Notifications V2 URL. - Deadline: Apple says to respond within 12 hours of receiving the
CONSUMPTION_REQUESTnotification. - Condition: answer only if the customer gave consent. If not, Apple says not to respond.
- Effect: the App Store uses your information to inform its refund decision. It is one input, not a veto.
- Outcome: a
REFUNDnotification when Apple refunds,REFUND_DECLINEDwhen it does not. - Automate it: RevenueDot's Refund Control sends the answer from rules you set once.
How the flow works#
- The customer asks for a refund. Apple documents several in-app entry points,
beginRefundRequestamong them. - Apple sends
CONSUMPTION_REQUESTto your server. Apple's notification type list describes it as the customer initiating a refund request and the App Store requesting consumption data. - Your server decodes the signed payload and reads the transaction ID. See App Store Server Notifications V2 for how to verify the payload.
- You call
PUTon the Send Consumption Information endpoint with the transaction ID. Apple answers202when it received the data. - Apple decides. If it refunds, you receive
REFUND. If not,REFUND_DECLINED.
Apple's endpoint answers 202 on success, 400 for an invalid request, 401 for a bad JWT, 404 when the transaction ID is not found, 429 over the rate limit and 500 for a server error to retry.
Two versions of the endpoint#
Apple documents two request bodies, and you should know which one you use.
| Send Consumption Information V1 | Send Consumption Information (current) | |
|---|---|---|
| Path | PUT /inApps/v1/transactions/consumption/{transactionId} |
PUT /inApps/v2/transactions/consumption/{transactionId} |
| Products | Consumables and auto-renewable subscriptions | Any product type, including non-consumables and non-renewing subscriptions |
| Refund preference | Numbers 0 to 3 | GRANT_FULL, GRANT_PRORATED or DECLINE |
| Usage data | Status, play time, account tenure, lifetime dollars, user status | A consumptionPercentage in milliunits, plus delivery status |
Sources: the V1 endpoint, the current endpoint and the ConsumptionRequest body. Apple does not mark V1 as deprecated on the pages we read. RevenueDot's Refund Control calls the V1 path today. If you build your own, check Apple's pages for the current guidance before you choose.
Consent comes first#
Apple is firm that you must obtain valid consent from the customer before sharing their personal data through this API, and that you, the developer, are solely responsible for getting it. Apple's guidance:
- Valid consent is freely given, specific, informed and unambiguous.
- Tell customers that you will give Apple some of their data to help review refund requests, and that they can withdraw consent at any time.
- Opt-in is a higher standard than opt-out.
- Do not use the App Tracking Transparency prompt for this consent. Apple says the two are unrelated.
- Answer your privacy label questions about the data you share.
The API rejects a customerConsented value other than true with HTTP 400. If you have no consent, send nothing.
Put the wording in your terms or privacy policy, and ideally in your onboarding. Apple cannot advise on the legal wording for your region. Ask a lawyer.
The V1 fields Apple wants#
All of these are required in a V1 body except refundPreference:
| Field | What it says |
|---|---|
customerConsented |
Must be true |
consumptionStatus |
0 undeclared, 1 not consumed, 2 partially consumed, 3 fully consumed |
deliveryStatus |
0 delivered and working, 1 quality issue, 2 wrong item, 3 server outage, 4 in-game currency change, 5 other |
platform |
The platform on which the customer used the purchase |
playTime |
Engagement buckets from 0 to 7: 0 undeclared, 1 is 0 to 5 minutes, up to 7 for over 16 days |
accountTenure |
The age of the customer's account |
lifetimeDollarsPurchased |
Total USD of purchases across platforms, in buckets |
lifetimeDollarsRefunded |
Total USD of refunds, in buckets |
sampleContentProvided |
true if you offered a free sample or trial first |
appAccountToken |
The UUID from the notification's transaction, or an empty string |
userStatus |
The status of the customer's account |
refundPreference |
0 undeclared, 1 you prefer a refund, 2 you prefer no refund, 3 no preference |
For any field you would rather not provide, Apple allows an "undeclared" value, such as 0, or an empty string for appAccountToken.
Your preference is a nudge#
Apple says your refund preference is one of several factors it weighs. It will not follow it blindly. Think about what is true and use it honestly:
- A renewal the customer did not notice, such as a trial that converted yesterday and was never opened. Preferring a refund is fair, and it keeps a customer you would lose anyway from becoming a chargeback or a bad review.
- A customer who used the product heavily over weeks. Sending real usage with a decline preference is fair.
- A server outage on your side. Say so in
deliveryStatusand prefer a refund.
Do not misstate facts. The consumption data comes from your records, and it is shared under the customer's consent.
Answer automatically with Refund Control#
Writing the notification handler, the JWT, the bucket mapping and the retry logic is real work and a lasting maintenance cost. RevenueDot's Refund Control does it with rules.

Before you start. Connect the App Store with the app's In-App Purchase key and turn on Version 2 notifications (guide). RevenueDot sends the answer with the same key. Make sure your terms tell customers that you share this data with Apple. RevenueDot sends nothing until you confirm consent in the dashboard.
Set the policies. Open Lifecycle, Refund control.
- Tick Customers agreed to share consumption data with Apple.
- Add policies from the templates: First purchase date (for example within 7 days), Platform, Recent renewal (renewed or converted from a trial in the last 24 hours), or Create your own from the audience builder (country, spend, email, attributes and more).
- Pick each policy's refund preference.
- Drag the policies into order and save.
| Preference | What Apple gets |
|---|---|
| Prefer full refund | Consumption information with refundPreference 1 |
| Prefer no refund | Consumption information with refundPreference 2 |
| Send consumption data only | Consumption information with no preference (0) |
| Do not respond | Nothing |
The first policy whose conditions match the customer decides. The default policy covers everyone else. Each card shows how many of your customers it would decide for.
What RevenueDot fills in from its own records:
| Field | How it is set |
|---|---|
consumptionStatus |
For subscriptions: 3 when the period is over, 2 when the customer opened the app after buying, 1 when they never did |
sampleContentProvided |
true when the customer had a free trial |
deliveryStatus |
0, because RevenueDot granted the purchase |
accountTenure, lifetimeDollarsPurchased, lifetimeDollarsRefunded |
From the customer's history, in Apple's buckets |
playTime |
0, unless your app sets the custom attribute rd_play_time_minutes |
userStatus |
1 for a known customer, or set rd_user_status to suspended, terminated or limited |
If Apple's API fails, RevenueDot retries after 5 minutes, 15 minutes and then every hour, and stops 5 minutes before the 12-hour deadline. A repeated notification for the same purchase is never answered twice.
See the results. Cards show the last 28 days: refund rate (approved divided by decided), refund request amount and request counts, split by approved and declined. A table lists each request with its policy, whether the answer went out and the outcome. The same data is in the API at GET /v2/projects/{project_id}/refund_requests.
Other stores. Google Play has no consumption API. A refund or chargeback arrives as a voided purchase and there is nothing to answer, so RevenueDot records it as an approved refund request and the cards cover every store. Stripe and Amazon refunds are recorded the same way.
Watch your first few requests in the table, and check that each one shows as answered before its deadline.
Build your own handler#
If you prefer to write it, the pieces are:
- Verify the notification (see the V2 post) and branch on
notificationType === "CONSUMPTION_REQUEST". - Return 200 at once, then process from a queue.
- Look up the customer by
appAccountTokenor the original transaction ID, and compute the fields from your records. - Sign an ES256 JWT with your In-App Purchase key (JWT rules:
audisappstoreconnect-v1, valid for up to 60 minutes). PUTthe body to the endpoint. Retry on 5xx and 429, with a stop before the 12-hour limit.- Store the outcome from
REFUNDandREFUND_DECLINEDso you can measure what your answers achieve.
Do it with RevenueDot#
- Create a free account and add your App Store app, with its In-App Purchase key.
- Set the notification URL in App Store Connect as Version 2, for production and sandbox.
- Add the consent wording to your terms, then tick the consent box in Lifecycle, Refund control.
- Create your policies, with a default policy last.
- Watch the Refund requests table for your first requests.
Start free on RevenueDot Cloud
FAQ#
How long do I have to respond to a CONSUMPTION_REQUEST?#
12 hours from the notification, per Apple's documentation. RevenueDot stops its retries 5 minutes before that deadline.
Do I have to respond to refund requests?#
No. Apple says to respond only when the customer consented to share data, and the response is optional otherwise. Apple uses your answer as one input to its decision, so ignoring it means Apple decides without your data.
Will Apple follow my refund preference?#
Not necessarily. Apple says your preference is one of a variety of factors. Use it honestly and measure the outcomes through REFUND and REFUND_DECLINED.
What does REFUND_DECLINED mean?#
Apple declined the refund request. It is the notification you get when a request that you may have answered ends without a refund. Apple's list of types defines it as the App Store declining a refund request.
Does Google Play have the same flow?#
No. Google Play has no consumption API. Refunds and chargebacks arrive as voided purchases and there is nothing to answer.
About RevenueDot. RevenueDot is an open-source (AGPL-3.0) backend for in-app purchases and subscriptions that works with the RevenueCat SDK. Start free on RevenueDot Cloud, free up to $10,000 in monthly tracked revenue, or self-host it with Docker and Postgres. Point the SDK's proxy URL at RevenueDot and keep your app code, your offerings and your customers. Read the quickstart or the code on GitHub.