
In brief
A .pkpass is a ZIP holding pass.json, a manifest of SHA-1 hashes, a PKCS#7 signature and your images. One mismatched byte and Wallet refuses the file without a useful error.
An Apple Wallet pass is a signed ZIP archive — the .pkpass file — not a document format. Wallet does not render an invalid pass with a warning: it refuses to add it, which is why most people meet this format by staring at a pass that simply will not open. This guide builds one from the four required parts, then covers the failure modes that produce a silent rejection.
Apple also ships Pass Designer for building a pass visually, and you can use it to prototype the layout. When the pass has to be generated by a server, you build the archive yourself.
01What a .pkpass file actually is
Rename any working pass to .zip, extract it, and you will find these pieces:
| File | Required | Purpose |
|---|---|---|
pass.json | Yes | The pass definition: identifiers, style, colors, barcode, fields |
manifest.json | Yes | SHA-1 hash of every other file in the package |
signature | Yes | PKCS#7 detached signature of manifest.json |
icon.png, icon@2x.png | Yes | The small icon shown in Wallet and notifications |
logo.png, strip.png, thumbnails | Per style | Optional artwork, must match Apple's sizes |
The manifest is the integrity layer, and Apple's build steps keep it deliberately plain: a flat JSON object mapping each source file's path to its SHA-1 hex digest. It never contains itself or the signature, and metadata files such as .DS_Store must not be listed either.
{
"icon.png": "6c4f1b2a4e2b7d3c9a5f8e1d0b6a3c2e4f5a6b7c",
"logo.png": "b1d2c3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0",
"pass.json": "9f8e7d6c5b4a39281706f5e4d3c2b1a09f8e7d6c"
}
The manifest must match the ZIP exactly
If
manifest.jsonlistslogo@2x.pngand that file is missing — or if a file exists in the ZIP but is absent from the manifest — the signature validation fails and Wallet rejects the pass. Regenerate the manifest after every image change.
02Build pass.json for the five pass styles
Every pass shares a set of top-level keys and then declares exactly one style. The shared keys are formatVersion (always 1), passTypeIdentifier, serialNumber, teamIdentifier, organizationName and description. The serializer that publishes updates also needs webServiceURL and authenticationToken.
The style dictionary is one of generic, storeCard, coupon, eventTicket or boardingPass. Each style exposes a different set of field regions — a boarding pass has primaryFields, secondaryFields, auxiliaryFields and backFields in fixed positions, while a generic pass uses the same regions with different intent.
The field counts are capped. In general a pass carries up to three header fields, one primary field, four secondary fields and four auxiliary fields; a boarding pass allows two primary fields and five auxiliary fields, and coupons, store cards and generic passes with a square barcode share four secondary and auxiliary fields between them. (PassKit guide)
{
"formatVersion": 1,
"passTypeIdentifier": "pass.com.example.loyalty",
"serialNumber": "member-8842",
"teamIdentifier": "ABCDE12345",
"organizationName": "Example Coffee",
"description": "Example Coffee loyalty card",
"backgroundColor": "rgb(67, 56, 202)",
"foregroundColor": "rgb(255, 255, 255)",
"labelColor": "rgb(224, 231, 255)",
"storeCard": {
"primaryFields": [
{ "key": "balance", "label": "Balance", "value": 12.5, "currencyCode": "USD" }
],
"secondaryFields": [
{ "key": "tier", "label": "Tier", "value": "Gold" }
]
},
"barcodes": [
{ "format": "PKBarcodeFormatQR", "message": "member-8842", "messageEncoding": "iso-8859-1" }
]
}
Two details trip people up. Colors use the rgb(r, g, b) string form, not hex and not an array. And serialNumber is your identity for the pass — reusing it for a different member overwrites the wrong card when you push an update.
03Generate the manifest and the PKCS#7 signature
Signing is the step that cannot be faked. You need a Pass Type ID certificate and its private key from your Apple Developer account, plus Apple's WWDR intermediate certificate.
- Hash every file except the manifest and signature. Run this from inside the pass directory:
for f in $(find . -type f ! -name 'manifest.json' ! -name 'signature' -maxdepth 1 | sed 's|^\./||'); do
printf ' "%s": "%s",\n' "$f" "$(shasum -a 1 "$f" | cut -d' ' -f1)"
done
- Assemble
manifest.jsonfrom those lines. It must be valid JSON with no trailing comma after the last entry — the loop above emits a trailing comma on purpose, so remove it or build the file with a script. - Sign the manifest, not the individual files:
openssl smime -binary -sign \
-certfile WWDR.pem \
-signer passcert.pem \
-inkey passkey.pem \
-in manifest.json \
-out signature \
-outform DER
- Zip the directory without a parent folder and without macOS metadata:
zip -r example.pkpass . -x '.*' -x '__MACOSX' -x '.*/'
- Verify before shipping. The signature must validate against the manifest you actually packaged:
openssl smime -verify -in signature -inform DER -content manifest.json -noverify
If that command prints Verification successful, the cryptographic half is done.
04Add and size the images Wallet expects
Wallet crops and scales the artwork you provide, so the number that matters is the space each image is allotted, measured in points. Images that do not match their allotted space are scaled and then cropped, and icon.png is required.
| Image | Allotted space | Notes |
|---|---|---|
icon.png | 29 × 29 points | Required; also used on the lock screen and in Mail |
logo.png | 160 × 50 points | Usually kept narrower than the full space |
strip.png | 375 × 123 points | 375 × 144 for store cards and coupons, 375 × 98 for event tickets |
thumbnail.png | 90 × 90 points | Keep the aspect ratio between 2:3 and 3:2 |
background.png | 180 × 220 points | Cropped slightly on all sides and blurred |
One point is exactly one pixel on a non-Retina display and two or three pixels on a Retina display, so ship the base file plus @2x and @3x variants. (PassKit guide)
Regenerate the manifest last
Edit images, then rebuild
manifest.json, then sign, then zip. Any earlier ordering leaves stale hashes in the manifest and produces a pass that looks finished but fails validation.
05Barcode rules that fail silently
The barcode is where a pass stops being a design exercise. Barcode scanners and software typically expect ISO 8859-1 (Latin-1) or Windows-1252, and Unicode is poorly supported by most systems, so a message carrying an emoji or a non-Latin name is the classic silent failure. Keep the barcode payload in ASCII and put the human-readable text in a field instead.
| Symptom | Cause | Fix |
|---|---|---|
| Pass adds but the barcode is blank | barcodes array missing or empty | Add at least one entry with a valid format |
| Barcode shows garbled text | message is not ISO 8859-1 or Windows-1252 | Keep message in ASCII and move the text into a field |
| No barcode on Apple Watch | Code 128 is the only entry, and watchOS does not support it | Add a QR or PDF 417 entry as a fallback |
| Older iPhone shows no barcode | Only the legacy barcode key is present | Emit barcodes as well; the array is used from iOS 9 |
Wallet supports QR, PDF 417, Aztec, Code 128, Code 39, Codabar, EAN 13 and ITF, and PassKit renders the first supported entry in the array. QR, PDF 417, Aztec and Code 128 are all valid on iOS 9 and later, so those four need no fallback. (Barcodes, PassKit guide)
Keep the payload short
Each format has a hard maximum, and real scanning conditions lower it — the guide's own example is a barcode on Apple Watch, in poor light, read by an inexpensive scanner. Test with the hardware your venue actually uses.
06Test on a device, then plan updates
Apple's documented test loop is to drop the pass onto an iPhone running in the Simulator, which shows the Add dialog if the pass is valid; run the full Add to Wallet flow on a real device before you ship to customers. AirDrop the file, or serve it from an HTTPS endpoint with the application/vnd.apple.pkpass MIME type so Safari offers the Add button. (Technical Note TN2302)
If the pass changes after issuance — a balance, a gate, a boarding time — you do not rebuild it on the device. You add webServiceURL and authenticationToken to pass.json, the device registers itself with your server and hands over a push token, and your server later sends a push notification that makes the device pull the updated pass. That is why authenticationToken must be long and random: it is the shared secret that authenticates every call to your update endpoints.
07Common errors and fixes
| Error | Likely cause | Fix |
|---|---|---|
| "Cannot add pass" with no detail | Manifest/signature mismatch | Rebuild manifest, re-sign, re-zip |
| Signature verifies but Wallet rejects | passTypeIdentifier does not match the certificate | Use the exact Pass Type ID from the portal |
| Pass appears unstyled | Background/foreground colors are hex strings | Use rgb(r, g, b) strings |
| Update never arrives | Wrong serialNumber or expired push token | Keep serial numbers unique and stable per pass |
The pattern behind almost every failure is the same: Wallet treats the pass as a signed artifact, so any change to any byte — including re-saving an image — invalidates the signature. Build a script that regenerates the manifest, signs it, and zips in one command, and you will stop debugging hashes by hand.
08Sources
- Building a Pass — https://developer.apple.com/documentation/walletpasses/building-a-pass (accessed 2026-10-09)
- Creating a Pass with Pass Designer — https://developer.apple.com/documentation/walletpasses/creating-a-pass-with-pass-designer (accessed 2026-10-09)
- Pass — https://developer.apple.com/documentation/walletpasses/pass (accessed 2026-10-09)
- PassKit Programming Guide: Creating Passes — https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/PassKit_PG/Creating.html (accessed 2026-10-09)
- Barcodes — https://developer.apple.com/documentation/walletpasses/pass/barcodes (accessed 2026-10-09)
- Technical Note TN2302: Passbook FAQ — https://developer.apple.com/library/archive/technotes/tn2302/_index.html (accessed 2026-10-09)
- Adding a Web Service to Update Passes — https://developer.apple.com/documentation/walletpasses/adding-a-web-service-to-update-passes (accessed 2026-10-09)
Validate and format pass.json before you hash it — a trailing comma there breaks the whole pass.
JSON Multi-tool SuiteFrequently asked questions
Why won't my .pkpass add to Apple Wallet?
Nine times out of ten the signature does not match the manifest, or the manifest lists a file that is not in the ZIP. Wallet rejects the pass silently in both cases.
Can I create a Wallet pass without an Apple Developer account?
No. Signing requires a Pass Type ID certificate issued from your Apple Developer account. You can build and test the JSON and images without it, but Wallet will not accept an unsigned pass.
Does the manifest use SHA-1 or SHA-256?
The manifest format uses SHA-1 hashes over each file in the package, which is separate from the SHA-1/SHA-256 discussion about Git object hashes.
Why does my barcode disappear on Apple Watch?
watchOS does not support Code 128. If Code 128 is the only entry in the barcodes array, the watch shows no barcode at all, so add a QR or PDF 417 entry as a fallback.
Why is my barcode not scanning?
The payload usually does not match what the scanner expects: barcode hardware typically reads ISO 8859-1 or Windows-1252, and Unicode is poorly supported. Keep the message in ASCII and move the readable text into a field.
Related articles
- How to format, validate and minify JSON
pass.json is strict JSON — the same rules that break API payloads break passes.
- Create a QR code that scans every time
Error correction, quiet zones and sizing for the barcode on your pass.
Related tools
- JSON Formatter
Pretty-print and re-indent pass.json.
- JSON Validator
Catch the trailing comma before signing.
- Base64 Encoder / Decoder
Inspect encoded payloads and barcode data.