Hello,
We’re developing a loyalty program using Apple Wallet passes, and everything works except the silent push update flow.
Context PassTypeIdentifier: pass.order.com.lealtad.xxxx
Device is physical (not simulator)
Pass is installed and visible in Wallet
Correct pushToken is stored in our backend
Push response from APNs:
StatusCode: 200 OK apns-id: DBFF9693-D053-AA20-D737-3B24414105F3 WebServiceURL is reachable: https://xxxxx.net
GET /passes/{passTypeIdentifier}/{serialNumber} endpoint is implemented and logs all calls
No call is received from Apple to GET /passes/... after the push
.pkpass is signed and installs correctly
push payload json Copiar Editar { "aps": { "content-available": 1 }, "serialNumbers": ["9a4c9376d4"], "updated": true } pass.json json Copiar Editar { "formatVersion": 1, "passTypeIdentifier": "pass.order.com.lealtad.xxxxx", "teamIdentifier": "xxxxxxxx", "serialNumber": "9a4c9376d4xxxxxx", "webServiceURL": "https://xxxxxx.net", "authenticationToken": "xxxxxxxxxxxxxxxxx", ... } We have verified: Pass is installed and contains the correct authentication token.
Pass registration via POST /devices/... happens and is logged.
Push notifications are sent successfully and received (APNs 200 OK).
Our endpoint GET /passes/... responds properly with Authorization: Bearer {{token}}.
Certificate used to send the push is the same type as the one used to sign the .pkpass.
What could cause Apple Wallet to ignore the push? We would appreciate guidance on what might be preventing Wallet from calling the webServiceURL after a valid push. Is there a way to trace why Apple is not triggering the request?
Thanks in advance for your help.
Best regards, José Ruiz
Hi @jruizr,
You wrote:
[...] What could cause Apple Wallet to ignore the push? We would appreciate guidance on what might be preventing Wallet from calling the webServiceURL after a valid push. [...] Is there a way to trace why Apple is not triggering the request? [...]
It appears there are multiple issues with the approach you describe here, and some things to verify:
- Issue 1: Wrong push payload format
- Issue 2: Missing intermediate endpoint
- Issue 3: Missing
apes-topicheader - Issue 4: Serial number mismatch
Issue 1: Wrong push payload format
Your current payload is incorrect. Apple Wallet passes use a completely empty app body. Wallet does not read serialNumbers, updated or content-available from the push payload. Those fields are ignored or may confuse the process.
Your payload (incorrect):
{
"aps": {
"content-available": 1
},
"serialNumbers": ["9a4c9376d4"],
"updated": true
}
Correct payload:
{
"aps": {}
}
The push token itself tells Wallet which device to wake up. That's all the payload needs to do.
Issue 2: Missing intermediate endpoint
This is very likely the root case of why GET /passes is never called. After receiving the push, Wallet doesn't directly call this endpoint—first it calls your device registrations endpoint to ask "which passes need updating?" See this post for more information:
Clarification on Secure Handling of authenticationToken for Wallet Pass Updates
https://developer.apple.com/forums/thread/814731?answerId=893576022#893576022
If GET /v1/devices/{deviceLibraryIdentifier}/registrations/{passTypeIdentifier} is not implemented, or returns an empty serialNumbers array or a malformed response, then Wallet silently stops and never calls GET /passes.
The required response format for the registrations endpoint is below:
{
"lastUpdated": "1749823600",
"serialNumbers": ["9a4c9376d4xxxxxx"]
}
Note: lastUpdated is a string tag (e.g., a Unix timestamp). Wallet send this back as passesUpdatedSince on subsequent calls so you only return passes updated since that moment.
Issue 3: Missing apes-topic header
When sending a push for a Wallet pass, the apes-topic header must be sent to your passTypeIdentifier, not your app's bundle ID.
apns-topic: pass.order.com.lealtad.xxx
apns-push-type: background (optional, but recommended)
Without this, APNs may route the push incorrectly even if it returns 200 OK. A 200 from APNs only confirms delivery acceptance, not correct routing.
Issue 4: Serial number mismatch
In your pass.json, a full value for the serial number is provided:
"serialNumber: 9a4c9376d4xxxxxx
However, in your push payload's serialNumbers array, the serial number value is truncated:
"serialNumbers": ["9a4c9376d4"]
While the payload field itself is irrelevant (Issue 1), this suggests there may be a truncation bug in your backend when storing/retrieving serial numbers. This could also just be a manual truncation for the forums post. Nevertheless, please verify your database stores and returns the full serial number in the registrations endpoint response.
Quick validation test
To isolate whether the registrations endpoint is the source of the issue, temporarily hardcode it to always return your serial number regardless of passesUpdatedSince:
{
"lastUpdated": "9999999999",
"serialNumbers": ["9a4c9376d4xxxxxx"]
}
Send a push and check if GET /passes is now called. If it is, your registrations endpoint logic (filtering by update time) is the bug.
Cheers,
Paris X Pinkney | WWDR | DTS Engineer