Skip to main content
When transferring an iOS app between Apple Developer Teams (e.g., selling an app or moving to a new organization), Sign in with Apple user identifiers (sub) change because they are team-scoped. Users who chose “Hide My Email” also receive new private relay email addresses under the new team. Without migrating these identifiers in Privy, affected users will lose access to their existing accounts.
You have 60 days from the date of the app transfer to complete the migration. After that, Apple’s migration endpoints become inactive. If you miss this window, the app must be transferred back and the process restarted. See TN3159: Migrating users after the 60-day app transfer period.

What changes during an Apple team transfer

What changesImpact on Privy
Every user gets a new team-scoped sub (subject identifier)Privy matches users by subject — logins will fail if subjects aren’t updated
”Hide My Email” users get a new @privaterelay.appleid.com addressEmail-based fallback matching fails, causing duplicate accounts
Both teams’ credentials remain valid during the 60-day windowNo immediate login disruption, but credentials must be updated before the window closes

Before the transfer (Team A)

Step 1: Export your Apple users from Privy

Use the Privy API to fetch all users for your app, then filter for users with apple_oauth linked accounts. For each matching user, extract their Privy ID, Apple subject, and email. For each Apple OAuth account, you will need:
  • Privy ID (privy_id)
  • Apple subject (the current sub stored in Privy)
  • Email (the email currently stored for the Apple OAuth account)
Save this list — you will need each user’s Apple sub in step 3 to generate transfer identifiers via Apple’s API. Temporarily remove Apple as a login method in the Privy dashboard configuration.
There is a race condition between when the app transfer completes (Apple starts issuing new sub values) and when Privy’s subject migration is run. If a user signs in during this window, their new sub won’t match any stored subject, and if they used “Hide My Email,” their new relay email won’t match either so we don’t do any automatic account merging. This results in a duplicate account being created.
This is especially important if:
  • Your users have wallets, balances, or other critical state tied to their accounts
  • A significant portion of your users use “Hide My Email” (private relay)
If your app has very few Apple sign-in users and you can execute the migration quickly, it may be acceptable to skip this step and accept the risk of needing to manually resolve duplicates.

Step 3: Generate transfer identifiers

Using Team A’s credentials, generate a transfer_sub for each user. Follow Apple’s guide: Transferring your apps and users to another team. Obtain an access token for Team A:
curl -X POST "https://appleid.apple.com/auth/token" \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=client_credentials' \
  -d 'scope=user.migration' \
  -d 'client_id=YOUR_CLIENT_ID' \
  -d 'client_secret=CLIENT_SECRET_SIGNED_BY_TEAM_A'
For each user, generate a transfer identifier:
curl -X POST "https://appleid.apple.com/auth/usermigrationinfo" \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -H 'Authorization: Bearer ACCESS_TOKEN_FOR_TEAM_A' \
  -d 'sub=USERS_CURRENT_APPLE_SUB' \
  -d 'target=TEAM_B_TEAM_ID' \
  -d 'client_id=YOUR_CLIENT_ID' \
  -d 'client_secret=CLIENT_SECRET_SIGNED_BY_TEAM_A'
Apple returns:
{
  "transfer_sub": "760417.ebbf12acbc78e1be1668ba852d492d8a.1827"
}
Save the transfer_sub alongside each user’s privy_id and old sub.

Step 4: Initiate the app transfer

Transfer the app in App Store Connect. Once Team B accepts, Apple begins issuing Team B-scoped identifiers.

After the transfer (Team B)

Step 5: Exchange transfer identifiers for new identifiers

Using Team B’s credentials, exchange each transfer_sub for the new team-scoped sub and (if applicable) the new private relay email. Follow Apple’s guide: Bringing new apps and users into your team. Obtain an access token for Team B:
curl -X POST "https://appleid.apple.com/auth/token" \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=client_credentials' \
  -d 'scope=user.migration' \
  -d 'client_id=YOUR_CLIENT_ID' \
  -d 'client_secret=CLIENT_SECRET_SIGNED_BY_TEAM_B'
For each user, exchange the transfer identifier:
curl -X POST "https://appleid.apple.com/auth/usermigrationinfo" \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -H 'Authorization: Bearer ACCESS_TOKEN_FOR_TEAM_B' \
  -d 'transfer_sub=TRANSFER_SUB_FROM_TEAM_A' \
  -d 'client_id=YOUR_CLIENT_ID' \
  -d 'client_secret=CLIENT_SECRET_SIGNED_BY_TEAM_B'
Apple returns the new team-scoped identifiers:
{
  "sub": "820417.faa325acbc78e1be1668ba852d492d8a.0219",
  "email": "[email protected]",
  "is_private_email": true
}
  • sub — the new Team B-scoped user identifier (present for all users)
  • email — the new private relay email (only present for users who used “Hide My Email”)
  • is_private_email — indicates this is a relay address
Save the new sub and email (when present) alongside each user’s privy_id.

Step 6: Build the migration CSV

Prepare a CSV with the following columns:
privy_id,old_apple_sub,email,new_apple_sub,new_email
did:privy:user1,001234.old-sub-a.5678,[email protected],820417.new-sub-b.0219,[email protected]
did:privy:user2,001234.old-sub-c.9012,[email protected],820417.new-sub-d.3456,
ColumnRequiredDescription
privy_idYesThe user’s Privy ID (e.g., did:privy:abc123)
old_apple_subYesThe current Apple subject stored in Privy (Team A’s sub)
emailNoThe email currently stored in Privy for this Apple account, used for verification
new_apple_subYesThe new Apple subject from step 5 (Team B’s sub)
new_emailNoThe new private relay email from step 5 (include when is_private_email was true)
For users who shared their real email address (not “Hide My Email”), leave new_email blank. Real email addresses are not team-scoped and don’t change during migration — Apple’s exchange response won’t include an email field for these users.

Step 7: Submit the migration to Privy

Provide the CSV to Privy support to run the Apple subject migration. This updates each user’s stored subject and, where provided, their stored email to the new Team B values. We’ll reach out once the migration is complete on our end.

Step 8: Update Apple OAuth credentials in Privy

This step can be done while waiting for the subject migration in step 7 to complete — they are independent. Update the app’s Apple OAuth configuration with Team B’s credentials:
  • Key ID — the Key ID for Team B’s Sign in with Apple private key
  • Private key — Team B’s .p8 private key file
  • Team ID — Team B’s 10-character Team ID
  • Client ID — this is typically the bundle ID and stays the same after transfer
The Key ID and private key can be updated directly in the Privy Dashboard. However, the Team ID and Client ID are read-only in the dashboard once users exist. Contact Privy support to update these fields.
Both teams’ credentials remain valid during the 60-day migration window, so this step doesn’t need to happen before the subject migration. However, it must be completed before the 60-day window closes or Apple login will stop working.

Step 9: Re-enable Apple login and verify

Once the subject migration (step 7) and credential update (step 8) are both complete, re-enable Apple as a login method in the Privy dashboard. Then verify the migration by testing with a small number of users:
  1. Have a user sign in with Apple through the app
  2. Verify they land on their existing account — same Privy ID, same linked accounts, same wallets and data
  3. If possible, also test with:
    • A user who used “Hide My Email” (private relay) — these are most likely to be affected
    • A user who shared their real email address
    • A brand new user signing up for the first time post-transfer
If anything is wrong, you can disable Apple login again while investigating.

Troubleshooting

Duplicate accounts were created

If users signed in during the migration window (between the transfer and the subject migration), they may have new duplicate accounts. Contact Privy support with the affected Privy IDs to resolve these.

The 60-day window has passed

If more than 60 days have elapsed since the transfer, Apple’s migration endpoints are no longer active. The app must be transferred back to Team A, and the process restarted from step 3. See TN3159: Migrating users after the 60-day app transfer period.

Apple documentation