Crossdeck found your customers — now name them
The rail discovery from move 1 gave Crossdeck a customer record for every Stripe customer it found — but each one is keyed only by its rail identity (a cus_…), with no developerUserId attached yet. Ready to be linked, but not yet linked. This step closes that gap: you read your own user table and tell Crossdeck, in one batched call, which user ID owns which rail customer.
POST /v1/migration/users, in batches of 1000
From your backend (with your secret key), send up to 1,000 rows per request. Each row carries your developerUserId plus whatever you know — rail keys, traits, and the entitlements to backfill:
await fetch("https://api.cross-deck.com/v1/migration/users", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CROSSDECK_SECRET_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
users: [
{
developerUserId: "user_847", // required
email: "[email protected]",
stripeCustomerId: "cus_Nx…", // the rail key Crossdeck discovered
entitlements: { pro: true }, // backfill current plan
},
// …up to 1000 rows per request…
],
}),
});
The rail-owned gate keeps you from double-granting
Each row's developerUserId is matched to the rail-keyed customer Crossdeck discovered — via stripeCustomerId (or an Apple / Google key) — fusing the two into one canonical customer with both identities. That's the link.
The clever part is what happens to the entitlements you assert. They pass a rail-owned gate, so you can safely send pro: true for everyone without fear of conflicting with the truth:
- If the row's Stripe customer holds a live subscription, the rail already owns that entitlement — the manual grant is skipped (no double-grant).
- If the subscription is lapsed, it's also skipped — the rail says they're not entitled, and that wins.
- Only customers with no rail behind them receive the asserted manual grant (a comp account, a legacy lifetime deal).
The endpoint requires a secret key and rejects publishable ones with a clear message — this is a privileged, server-only operation.
Rail customers, now linked to your users
After the import runs, the customers Crossdeck discovered now carry your user IDs, and their plan is backfilled — live subscriptions deferring to the rail, manual grants only where there's no rail to defer to. Your existing book is now Crossdeck's, correctly.
cus_Nx… is now user_847, Pro — granted by the live Stripe subscription, not double-granted by the import.