To add a pass to Apple Wallet you need a certificate from Apple before you need a button.
Two things have to exist before you can add a pass to Apple Wallet: a pass type identifier and a Pass Type ID certificate, both created in your Apple Developer account. A pass is a signed bundle, and an unsigned one does not install, so there is no way to skip them and wire up the button first. Apple's Wallet Passes documentation, read on 3 October 2026, lists building a pass as five steps in this order: the source files, the identifier, the signing certificate, the signature, the bundle. There is also no Apple Wallet on Android, so an app that serves both phones needs a second, unrelated integration. If what you want is to take money rather than issue a card, that is payments and money and a different job.
This page goes in that order: the identifier and the certificate first, then what the person on the phone is asked, then the Android half. Every field name, console step and limit below comes from Apple's or Google's own documentation opened on 3 October 2026, with the page named beside it.
See which prerequisites apply to your passThe short version
The certificate is the job the button is twenty minutes.
A pass type identifier is a reverse DNS string you register in the Certificates, Identifiers and Profiles area of your Apple Developer account. A Pass Type ID certificate is generated against that identifier from a certificate signing request you make, and its private key has to sit on whatever signs each pass. Both need a paid Apple Developer Program account, which Apple's enrollment page, read on 3 October 2026, puts at 99 USD a year.
After that a pass is just a directory: pass.json, images, a manifest.json holding the SHA1 hash of every file, a detached PKCS #7 signature of that manifest, zipped and renamed from .zip to .pkpass. Apple requires six keys in pass.json: description, formatVersion set to 1, organizationName, passTypeIdentifier, serialNumber and teamIdentifier.
The identifier and the certificate, in order
Start in Certificates, Identifiers and Profiles in your Apple Developer account. Select Identifiers, click Add, choose Pass Type IDs, click Continue, then enter a description and the reverse DNS string. Apple's own example is com.example-company.passes.ticket.event-4631A, which shows the scope: one identifier covers a group of related passes, such as the tickets for one event, not a single pass. Passes inside a group are separated by serial number, and Apple is blunt about the consequence. Adding a pass with the same pass type identifier and serial number as one already on the device overwrites the old one.
Then the certificate. Generate a certificate signing request first, because you upload it during the process. Back in the same area, select Certificates, click Add, choose Pass Type ID Certificate, name it, pick your pass type ID from the dropdown, upload the request, then generate and download the certificate to the machine that will sign passes. Set passTypeIdentifier in pass.json to the identifier you registered and teamIdentifier to the Team ID of the account that registered it. Apple lists a mismatch between either of those and the signing certificate as a reason a pass fails to build.
Signing needs no SDK. Write a manifest.json whose keys are the file paths inside the pass and whose values are the SHA1 hash of each file, including files in subdirectories. Create a detached PKCS #7 signature of that manifest with the certificate's private key. Save it at the top level in a file called signature. Zip the directory and change the extension to .pkpass. Apple's list of common failures is the first thing to read when it does not work: a missing required key, an identifier that does not match the certificate, an expired certificate, a file left out of the manifest, or a .DS_Store that crept in.
None of this can happen inside the app. The private key cannot ship in a binary that strangers install, so pass generation belongs on a server you control. That is the surprise for anyone expecting a client library, and the reason a Wallet pass is a backend feature wearing a front end costume. One note if you arrived here from an older answer: the pass format and the signing steps now sit in Apple's Wallet Passes documentation, while the classes your app calls stay under PassKit, so searching PassKit alone will not find the build steps.
Apple Developer Documentation, Wallet Passes: Building a Pass (read 3 October 2026)
What the person on the phone is asked
In the normal case, nothing. There is no permission to add a pass on iOS. You show an Add to Apple Wallet button, the system shows the pass, the person taps Add, and that tap is the consent. Inside an app that is PKAddPassButton for the button and PKAddPassesViewController for the sheet. Check PKAddPassesViewController.canAddPasses() before you show either, not PKPassLibrary.isPassLibraryAvailable(): Apple's note on that one says a device can have a pass library and still be unable to add passes.
To add several at once, PKPassLibrary.addPasses(_:withCompletionHandler:) hands back one of three statuses: didAddPasses, shouldReviewPasses or didCancelAddPasses. Refusal is the third, and it is an ordinary result rather than an error. If you get shouldReviewPasses you have to present PKAddPassesViewController yourself so the person can review each pass before it lands.
One permission does exist, and it is new, which is why almost nothing written about this mentions it. Apple's Distributing and updating a pass article, read on 3 October 2026, describes a backgroundAddPasses capability requested once through PKPassLibrary.requestAuthorization(for:completion:). It shows a one time prompt. If the person agrees, addPasses puts passes into Wallet in the background and tells them with a system notification instead of an on screen confirmation. Call the same method again later and it does not prompt again, it returns the current status, and people can change their answer in Settings. The capability, the request method and the status cases, which are authorized, denied, notDetermined and restricted, are all marked iOS 26 and later. Their reference pages carry the symbol names and no description text as of 3 October 2026, so treat the article as the explanation and the reference as the signature.
Refusal costs you the silent add, not the pass. If you never request the capability, addPasses prompts before each pass, which is the behaviour every older tutorial describes. Declaring is separate from asking: reading passes already in Wallet needs the Pass Type IDs entitlement, com.apple.developer.pass-type-identifiers, which you get by enabling the Wallet capability in Xcode and listing the identifiers your app may touch. Adding a pass needs no entitlement. Apple also notes that the automatic in-app add requires your app to be installed and running on the device, which rules it out for a pass somebody gets by email. If the pass is a ticket scanned at a door, the flow around it matters more than the add, and that is the event check in app problem.
Check it
What you have to go and get
Pick what your pass has to do. Everything below has to exist before the button does anything.
3 things to get, 1 of 6 picked
- A pass type identifier, registered under Identifiers in Certificates, Identifiers and Profiles
- A Pass Type ID certificate, and its private key on whatever signs each pass
- Server side code that writes pass.json, hashes every file into manifest.json and signs it
There is no Apple Wallet on Android, there is Google Wallet
Apple lists PassKit on Apple platforms only. PKPassLibrary is published for iOS, iPadOS, Mac Catalyst, macOS, visionOS and watchOS, PKAddPassesViewController for iOS, iPadOS, Mac Catalyst and visionOS. There is no Android entry and no Apple Wallet app on Android. The Android half of a cross platform app is a different product with its own account, its own signing and its own button. None of the Apple work carries over.
The shape is different too. Create an issuer account in the Google Pay and Wallet console, generate REST API and Android SDK credentials, then create a Passes Class, which is a template, and a Passes Object, which is the individual pass, and encode both in a JSON Web Token. In the app, add com.google.android.gms:play-services-pay, call PayClient.getPayApiAvailabilityStatus with PayClient.RequestType.SAVE_PASSES, and only show the button when the result is PayApiAvailabilityStatus.AVAILABLE. Google names an out of date Android or Google Play services version, and Google Wallet being unavailable in the person's country, as reasons it will not be.
Saving is PayClient.savePasses, which launches the save screen and returns through onActivityResult as RESULT_OK, RESULT_CANCELED or PayClient.SavePassesResult.SAVE_ERROR, with the error text in PayClient.EXTRA_API_ERROR_MESSAGE. There is no runtime permission anywhere in that flow: the save screen is the consent and refusal arrives as RESULT_CANCELED. The button is a fixed size, 48 dp high and at least 200 dp wide, using the vector asset Google supplies.
Two Google specifics catch people out. Until you are granted publishing access, every pass you create carries the text [TEST ONLY] in its title, which is fine in development and awkward in a demo. And the Add to Google Wallet link, the email and web route, is a URL of the form pay.google.com/gp/v/save/ followed by a signed token; Google's page for it, read on 3 October 2026, says the save can only start in the context of a logged in Google identity, and that the safe length of the encoded token is 1800 characters, above which a browser may truncate the link. A signed .pkpass has no account requirement at all. Tapping a pass at a terminal is a separate story: on Apple's side the payload is capped at 64 bytes and needs a special entitlement issued by Apple, on Google's it is Smart Tap, a property of the Passes Class. Either way it needs real hardware and a real reader, so it belongs with add nfc.
Google Wallet API documentation, Issuing passes with the Android SDK (read 3 October 2026)
What you can test, and what needs a real device
You can get further on a simulator than people expect. Apple's Building a Pass article says to test a pass by dropping it onto an iPhone running in Simulator, and that Wallet shows the add pass dialog if the pass is valid. That exercises the signature, the required keys and the image set. Do it before you write a line of app code, because an invalid pass fails quietly in a way that looks exactly like an app bug.
Two things will not work there. The NFC payload on a pass is defined by Apple as what the device sends to an Apple Pay terminal, so a tap has to be tested on hardware at a reader, and the entitlement has to be issued first. And pass updates cannot be tested outside production: Apple states that a push notification for a pass update works only in the production environment. That one costs people an afternoon.
Updates are simpler than they sound. An updated pass is a new pass with the same pass type identifier and serial number. To push rather than hand out, add webServiceURL and authenticationToken to pass.json and implement Apple's pass update web service: the device registers with a device library identifier and a push token, your server sends a push when something changes, and the device asks for the new copy. The push uses the same certificate and private key that signed the original and an empty JSON dictionary as its payload. You can change anything except the authentication token and the serial number.
Outside an app, the add to wallet button is Apple's badge artwork, and the rules for it are strict: use Apple's artwork rather than your own version, SVG for web and email, EPS for printed material carrying a scannable code, and clear space of at least a tenth of the badge height. A bundle of passes is a .zip of .pkpass files renamed to .pkpasses, served as application/vnd.apple.pkpasses and capped at 10 passes or 150 MB. Whether any of this earns back the certificate work is a business question rather than a technical one, and it lands in loyalty and monetisation.
What each platform makes you get first
| What you need | Apple Wallet on iOS | Google Wallet on Android | Harder side |
|---|---|---|---|
| A developer account | Apple Developer Program, 99 USD a year | Google Wallet API issuer account, no fee published in the onboarding guide | Apple |
| A signing key | Pass Type ID certificate, from a signing request you generate | Google Cloud service account key, used to sign a JSON Web Token | even |
| Approval before a live pass | none stated for passes | publishing access, until then every title carries [TEST ONLY] | |
| What the person is asked | Add on a sheet, or one prompt if you want silent adds | nothing, the save screen returns a result code | Apple |
| Adding from email or a link | yes, the signed .pkpass installs itself | yes, but only for a signed in Google account |
Building this into an app you own
A Wallet pass is server work with a button on the end. One endpoint that takes a booking or a loyalty record, writes pass.json, hashes the files, signs the manifest and returns the .pkpass is the whole requirement, and the client side can be as little as opening a URL. Apple's badge guidelines say people can add a pass straight from a web page or an email opened on iPhone, so a link to the signed file works with no PassKit code in your app at all.
Newly is an AI app builder: you describe the app in plain English and it writes a real React Native and TypeScript project you own, runs it on cloud iOS and Android simulators while it builds, and ships it to TestFlight and to Google Play internal testing. It is $25 a month on effort-based credits, with no free plan. Apps start local-first, with data on the phone and no server, and the agent adds Newly Backend when the app needs accounts, shared data or payments; that is where the API service, the Postgres database per environment and the file storage come from, which is what pass signing needs. The identifier and the certificate are still yours to create in your own Apple Developer account, and nothing automates that.
So the honest split. The agent can build the screens, the record a pass is generated from, the endpoint and the link out. You register the pass type identifier, make the signing request, generate the certificate and keep the private key somewhere sensible. Sign one minimal pass by hand and drop it on a simulator first. If that dialog appears, the hard half is done.
Questions people ask about Wallet passes
A pass type identifier and a Pass Type ID certificate from your Apple Developer account, and somewhere server side to sign with. The pass is a directory holding pass.json, images, a manifest.json of SHA1 hashes and a detached PKCS #7 signature, zipped and renamed to .pkpass. Apple requires six keys in pass.json: description, formatVersion set to 1, organizationName, passTypeIdentifier, serialNumber and teamIdentifier. Without a valid signature the pass does not install.
Describe the pass before you build the app
Write down which group the pass belongs to, what its serial number comes from, where the private key will live and what changes after you issue it. Then register the identifier, generate the certificate, and sign one pass by hand before any app code exists.
Start building