programId and externalId makes the button idempotent: an existing pass is reused, never duplicated.
Issue with context. Create new pass carries programId, walletType (APPLE_WALLET or GOOGLE_WALLET), your externalId, customer details for personalization, and the customer’s current loyaltyState, such as pointsBalance and currentTierId. The pass is correct the moment it lands in the wallet. An optional payload sets what the pass carries for in-store scans; see In-store. Returns 201 with the pass id.
Two link endpoints. Generate Apple Wallet installation link returns a .pkpass download URL valid for roughly 15 minutes. Generate Google Wallet save link returns a save link valid for roughly an hour. Each response carries the exact deadline in expiresAt. Request links at click time, and never cache them.
Activation is confirmed for you. When the customer adds the pass, it flips from PREACTIVE to ACTIVE and a signed PASS_STATUS_CHANGED webhook delivery tells your backend. Write it back to your customer record from there.
Don’t want to build this flow? The wallet button
widget does the same thing with no API
code. Stell hosts the button, the enrollment page, and the pass issuing.