iOS only. App2App supports iOS with Apple App Attest today. Android isn’t supported yet, so use Coinbase-hosted Onramp for Android.
Prerequisites
- Get allowlisted. Contact the Coinbase team to enable your CDP project for App2App and register your iOS App Attest identifier (
teamID.bundleID). - Add your redirect host to the domain allowlist. In CDP Portal, under Payments → Onramp & Offramp, add the host of the
redirectUrlyou’ll use. See Security Requirements for supported formats. Requests with aredirectUrlhost that isn’t allowlisted are rejected. - Use a physical iOS device. App Attest requires the Secure Enclave, which the iOS Simulator doesn’t have. This applies to sandbox testing too, not just production.
- Set up Apple’s DeviceCheck framework. App2App calls
DCAppAttestServicedirectly: you’re working with Apple’s APIs, not a Coinbase SDK. You’ll need the App Attest capability enabled in Xcode (requires an Apple Developer Program account) and a deployment target of iOS 14+. See Apple’s DeviceCheck documentation, the App Attest setup guide, and theDCAppAttestServicereference. - Add the Coinbase capability scheme so your app can detect whether the installed Coinbase app supports App2App:
canOpenURL, or the call always returns false. Only Coinbase app versions that support App2App register this particular scheme, which is what makes the check in Step 5 a valid capability check rather than a plain “is Coinbase installed” check.
None of the attestation, challenge, or session requests below need a CDP API key or Authorization header; your app’s attestation is the credential. Confirming settlement is the exception: webhooks require a webhook subscription on your CDP project, and querying the transactions API directly requires a CDP API key JWT.
What is App Attest? App Attest is Apple’s on-device mechanism for proving that a request comes from your genuine, unmodified app running on real hardware. It uses a hardware-backed key stored in the device’s Secure Enclave. You call Apple’s
DeviceCheck framework directly; Coinbase verifies the result server-side.Setup steps
Step 1: Register your app’s attestation key
Do this once per install. Persist the registration locally, scoped to your CDP project ID. You don’t need to repeat it for every purchase. If a later purchase assertion fails because the key is invalid, clear your local registration, register a new key, and retry once.1
Create a registration challenge
{ challenge, expiresAt }. challenge is base64url-encoded (unpadded).2
Attest your key with Apple
Call Decode the challenge before hashing it. Hashing the base64url string directly fails verification. Apple returns
DCAppAttestService.attestKey with:{ keyId, attestation }.3
Register the public key
appId (the matched teamID.bundleID identifier), keyId, platform: "ios", and attestedAt.Step 2: Create a purchase challenge
The response is
{ challenge, expiresAt }. The challenge expires after 5 minutes.
This endpoint doesn’t return a quote or eligibility details. To surface accurate information to the user before handoff, pull from:
- Buy Config: supported countries and the payment methods available in each (plus US state-level subdivisions).
- Buy Options: supported assets, networks, and fiat currencies, including the min/max limit for each payment method.
- Buy Quote: an actual fee breakdown and total for a specific amount, asset, and payment method.
Step 3: Generate a purchase assertion
CallDCAppAttestService.generateAssertion with the key you registered in Step 1 and:
{ keyId, assertion }.
Step 4: Create the session
onrampUrlcontains a session token that controls the destination, asset, network, and redirect for this purchase. Don’t append or modify query parameters on it.- This call is idempotent on the challenge: if you don’t receive a response (for example, due to a dropped connection), retry it. Retrying an already-attested challenge returns the same URL rather than an error.
- The session token expires 15 minutes after it’s issued.
Step 5: Detect the Coinbase app and open the purchase
Before showing or enabling your “Pay with Coinbase” button, check whether the installed Coinbase app supports App2App:- If this returns
true, open theonrampUrlfrom Step 4. - If it returns
false, either the Coinbase app isn’t installed or the installed version doesn’t support App2App yet, so fall back to Coinbase-hosted Onramp instead. - Open
onrampUrlexactly as returned. Don’t open thecom.coinbase.cdp.onramp://scheme yourself; it’s only used for the capability check above.
Step 6: Handle the return to your app
The Coinbase app redirects back to yourredirectUrl as a universal link with one of three outcomes. There’s no reason code on any of them; the query parameters below are everything you get:
handshakeNonceis reserved for a future verification step. You don’t need to do anything with it today; it’s safe to ignore. BothhandshakeNonceandtransactionIdare only included onsuccesstoday; Coinbase plans to also include them onerror/cancelledwhen available.errorandcancelledcarry no detail about what went wrong or how far the user got. If you need to distinguish “not eligible” from “transaction failed,” you can’t do it from the redirect alone.- If the user isn’t already signed in to Coinbase, they’ll sign in or create an account (and complete KYC if they’re new) before the purchase continues, all inside the Coinbase app, with no separate step needed from you.
Step 7: Confirm the transaction
- Follow the Onramp & Offramp Webhooks guide to create a webhook subscription for
onramp.transaction.success(and.created/.updated/.failedif you want the intermediate states). - Match incoming events to your user with
partnerUserRef; it’s included in every payload. - Treat a transaction as settled only when the event is
onramp.transaction.success(or, if you’re inspecting the payload directly,statusisONRAMP_TRANSACTION_STATUS_SUCCESS). - App2App transactions include
isAppToApp: true, so you can distinguish them from other purchase flows if needed.
partnerUserRef you sent in Step 2:
- Match transactions using
partnerUserRefplustxHash, purchase currency, and amount. The redirect’stransactionIddoesn’t match thetransactionIdreturned by this endpoint: they’re generated by different systems for the same purchase, so don’t use them to join records, even though the redirect’s value now looks like a real ID on a live purchase rather than a placeholder. - Sandbox (dry-run) sessions are the exception to both of the above; see Sandbox testing.
Sandbox testing
You can exercise the entire flow (registration, challenge, session, handoff, and redirect) without moving real funds, using an Xcode debug build or TestFlight’s sandbox App Attest environment. No production App Attest entitlement is required to test.1
Use a physical device
The iOS Simulator can’t attest: there’s no Secure Enclave. You need a physical device for every step that involves App Attest, in sandbox and production alike.
2
Prefix partnerUserRef with sandbox-
Send a
partnerUserRef like sandbox-user-1234 on the purchase challenge in Step 2. This is what puts the session into sandbox mode; there’s no separate sandbox flag, endpoint, or allowlist. Once your app is allowlisted for App2App at all, the sandbox- prefix is all you need.3
Run the flow with a development or sandbox App Attest key
Xcode debug builds and TestFlight’s sandbox App Attest environment are both accepted directly: register and attest exactly as you would in production. The one restriction: a development or sandbox App Attest key can only create a session when
partnerUserRef has the sandbox- prefix. Using one of these keys without the prefix is rejected.4
Confirm the sandbox purchase completes
The user experience is identical to a real purchase: sign-in, confirmation, and redirect all run normally. No funds move. The redirect still includes a
transactionId, generated for that sandbox purchase only, but unlike a live purchase, no webhook fires and no row appears in the transactions API for a sandbox session; the redirect is the only confirmation signal you’ll get in sandbox.- Keep the
sandbox-prefix until you’re ready to move real funds; it’s independent of which App Attest environment (development, sandbox, or production) you’re using. - Buy Options, Buy Quote, and Buy Config responses are estimates in both sandbox and production. After sign-in, Coinbase applies the user’s actual limits and eligibility.
- Apple Pay sandbox testing isn’t available yet, so sandbox purchases can’t currently exercise the Apple Pay payment sheet.
Errors
What to read next
- FAQ: Common questions from partners integrating App2App
- Onramp & Offramp Webhooks: Full webhook setup and payload reference
- Security Requirements: Domain allowlist requirements for your redirect URL