What you need before starting

The performance test is sandbox-only. You need an active sandbox original transaction ID for the app and subscription product you want Apple to test. Do not use an app transaction ID, a production transaction ID, or a transaction from another bundle.

  • A sandbox realtime URL configured through Apple's sandbox StoreKit API host.
  • A published sandbox runtime snapshot with valid sandbox messages and offers.
  • An active sandbox original transaction ID for an auto-renewable subscription.
  • A runtime path that can return a valid response without reaching App Store Connect.
  • Logs for latency, response shape, selected rule, fallback behavior, and request failures.

Timing note: Apple indicates performance-test execution and result availability can take time. In practice, plan for polling and allow up to 1 hour before treating a pending test as stuck.

Initiate the test and fetch the result

The performance-test endpoints are available on the sandbox StoreKit host. A passing result is required before configuring the production realtime URL.

POST https://api.storekit-sandbox.apple.com/inApps/v1/messaging/performanceTest Authorization: Bearer {in_app_purchase_key_jwt} Content-Type: application/json { "originalTransactionId": "2000000123456789" }

The initiate response returns a request identifier. Use that identifier to poll the result endpoint.

GET https://api.storekit-sandbox.apple.com/inApps/v1/messaging/performanceTest/result/{requestId} Authorization: Bearer {in_app_purchase_key_jwt}

Apple's configuration includes a response-time threshold. The sandbox documentation has referenced roughly 700ms, but your parser should read the threshold from the returned config instead of hardcoding assumptions.

How to read the result

The result response is not just pass or fail. It tells you how many requests are still pending, how often the runtime responded successfully, what the response times looked like, and which failures were counted.

FieldMeaningWhat to do
resultOverall test status, such as PASS.Production URL setup should wait for a passing result.
numPendingNumber of Apple test requests still not accounted for.Keep polling until pending reaches 0 or the run clearly expires.
successRatePercentage of test requests with valid successful responses.Investigate invalid response bodies, timeouts, and auth rejects.
responseTimesLatency distribution for your realtime endpoint.Optimize cold starts, KV reads, signing, and rule evaluation.
failuresFailure categories with counts.Map each failure to a runtime log event before retrying.

Sample result JSON

This example is illustrative, but it shows the shape you should make visible in your control plane. Operators need to see more than a pass badge.

{ "target": "https://runtime.retainkit.dev/apple/retention/app_9f21/sandbox", "result": "PASS", "numPending": 0, "successRate": 100, "config": { "numRequests": 50, "responseTimeThreshold": 700 }, "responseTimes": { "min": 42, "p50": 84, "p95": 162, "max": 231 }, "failures": {} }
RetainKit flow builder showing a published sandbox retention flow
Run the test against the same published sandbox flow Apple will exercise.

What PASS does not mean

Does not

Approve production messages. A sandbox PASS only proves your runtime can respond in time — it says nothing about production message review state.

Does not

Prove the production URL is set. A passing sandbox test is required before you can configure the production URL, but it is not the same as configuring it.

Does not

Validate every product and locale rule. The test uses one sandbox transaction. Rules covering other products, locales, or subscription groups need separate review.

A sandbox PASS is a gate, not a green light. Production readiness requires approved messages, configured production URL, and verified runtime behavior for each locale and product your rules cover.

Common errors and fixes

Apple's docs describe structured error types. In day-to-day setup, teams also run into numeric StoreKit-style errors. These two are especially common:

4000211 Invalid performance test request 4000006 Invalid transaction id
  • Invalid performance test request. Confirm the request body uses the expected transaction field and that the sandbox realtime URL is already configured.
  • Invalid transaction id. Use the original transaction identifier from an active sandbox auto-renewable subscription, not a renewal transaction, app transaction ID, production ID, or family-sharing unsupported transaction.
  • Existing test run. Wait for the current run to finish before starting another one.
  • Timeouts. Remove App Store Connect reads from the request path, precompile config, and keep signing work bounded.
  • Invalid responses. Ensure the response returns exactly one allowed branch: message, promotionalOffer, alternateProduct, or the appropriate fallback behavior.
  • Environment mismatch. Sandbox tests must use sandbox messages, sandbox offers, sandbox keys, and the sandbox realtime URL.

Sources

Configure the realtime URLWhy messages stay pendingAPI guide