What a Wallet pass actually is
An Apple Wallet pass is a small signed bundle of files that a phone stores and can present later — a boarding card, a loyalty card, a coupon, an event ticket. From an integration point of view it is closer to a signed data package than to an app: there is no executable code inside it, and everything the phone renders comes from a JSON dictionary plus a handful of images.
Ambimat works with passes from the reader side. If you are building a terminal that has to recognise a pass over NFC — Apple’s Value Added Services flow — it helps to understand how the pass is put together on the issuer side, because the same identifiers you provision into the pass are the ones your reader will be matching against.
The three moving parts
A Wallet deployment has three pieces, and they are usually built by different people:
- The package format. A
.pkpassbundle you generate and sign. This is the part covered below. - A web service on your server. Wallet calls it to fetch an updated copy of a pass after you signal that something changed.
- A device-side API. Used by your own iOS app if you want it to add, inspect or remove passes in the user’s library.
You do not need all three. A coupon that never changes needs only the first. A transit or membership card that has to stay current needs the first two.
How a pass stays up to date
The update path is worth understanding before you design your backend, because it is pull-based rather than push-based in the way people usually assume:
- Your server generates and signs the pass. The user installs it from an email, a link, or your app.
- Later, something changes — a balance, a gate number, a tier.
- Your server sends a push notification. That notification carries no payload of interest; it is only a nudge.
- The device responds by calling your web service and asking for the current version of that pass.
- Your server returns the freshly signed bundle, and the device replaces its stored copy.
The practical consequence: the push is a trigger, not a delivery mechanism. Your web service still has to be able to render and sign the pass on demand, and it has to be available whenever a device decides to ask.
Inside the .pkpass bundle
A pass file is a ZIP archive with a .pkpass extension. Localised variants live in the standard .lproj directory layout, so the same bundle can carry several languages. At the top level you will find:
pass.json |
The dictionary that defines the pass — its type, its fields, its colours, its barcode, and the identifiers that tie it to your pass type ID. This is where almost all of the design work happens. |
manifest.json |
A map of every other file in the bundle to that file’s SHA-1 hash. The manifest itself and the signature are the only two files excluded. |
signature |
A detached PKCS #7 signature over manifest.json, produced with your pass type certificate. |
icon.png |
Shown in notifications, on the lock screen, and in emails that carry the pass. iOS applies its own rounded corners and shine. |
logo.png |
Top-left artwork on the face of the pass. |
strip.png |
The band that sits behind the primary fields. |
background.png |
Full-bleed background artwork for pass styles that support it. |
footer.png |
Artwork near the barcode. |
thumbnail.png |
A secondary image on the face of the pass — a cardholder photo on a membership card, for example. |
Which images are actually used depends on the pass style you choose; a coupon and a boarding pass do not render the same slots.
The integrity model, and why builds break
The manifest-and-signature pair is the whole security model, and it is also the most common source of “the pass will not install” during development. Three things follow from it:
- Hash first, sign second. The manifest must be generated after every asset is final. Regenerating a PNG after hashing invalidates the bundle.
- Every file must appear. A resource present in the ZIP but missing from the manifest is a failure, not a warning.
- Zip the contents, not the folder. The files must sit at the archive root. Archiving the enclosing directory is the single most frequent packaging mistake.
Because the signature is detached and covers only the manifest, verifying a pass is cheap: check the signature, then re-hash the files and compare. That is what the device does before it will let a pass into the library.
Where this connects to reader-side work
For an Ambimat terminal integration the fields that matter are the ones that survive the NFC exchange — the pass type identifier and the merchant-specific payload your reader is provisioned to request. The visual keys in pass.json are irrelevant to the reader; the identifier structure is not. If you are specifying a loyalty or ticketing terminal, agree the pass type identifier and the payload format with whoever is issuing the passes before hardware selection, not after.
Authoritative reference
Apple’s own documentation is the normative source for key names, pass styles and certificate handling, and it changes independently of anything written here. Rather than restating it, refer to:
- Wallet Passes — Apple Developer Documentation, for the current pass.json key reference and pass styles.
- PassKit Package Format Reference (archived), for the historical bundle-layout description this page summarises.
This page is Ambimat’s own explanation of how the format fits together. It does not reproduce or replace Apple’s documentation.