All articles
// developers

Apple Wallet Pass Not Updating? Every Real Cause, Checked in Order

APNs returns success on every push, and the pass on the phone never changes. Every place that actually breaks it, checked in the order Wallet hits them.

Updated
Apple Wallet Pass Not Updating? Every Real Cause, Checked in Order
Julio Song
6 min read

A support agent at a regional coffee chain has fielded the same complaint four times this week. A loyalty pass in a customer's Apple Wallet still shows 3 punches after a purchase that should have pushed it to 9. The engineering team pulls the logs and finds nothing wrong: every push to APNs came back with a 200, the pass type identifier matches, and the code hasn't changed since the pass updated correctly in July.

That gap between "APNs said yes" and "the phone changed" is where most Wallet update bugs live, and it's worse than a normal failure because nothing throws an error. A silent push either works or it doesn't, and APNs will tell you it delivered the notification either way. Here's where it actually breaks, in the order to check, before you resign yourself to Wallet just being unreliable.

Confirm the device actually registered

Before a push can do anything, the device has to have registered for updates on that specific pass, and that step fails more often than it gets blamed for. When Wallet adds a pass with a webServiceURL, it sends a POST to your registration endpoint carrying the device's push token, authenticated with the authenticationToken from pass.json in an ApplePass authorization header. Get that token wrong, whether it's a typo in how you generated it or a mismatch between what you signed into the pass and what your server checks against, and the endpoint returns 401. Wallet doesn't retry and doesn't surface anything to the user. The pass installs looking completely normal, and it's simply never on your list to push to.

Check this first, not last. Pull your server logs for a POST to /v1/devices/{deviceLibraryIdentifier}/registrations/{passTypeIdentifier}/{serialNumber} around the moment the customer installed the pass. No entry, or a 401 where you expected a 201, means every step after this one is irrelevant. You're not failing to push an update. You never had anyone to push it to.

Production, not sandbox

This is the cause that costs developers the most time, because it looks exactly like success. A developer working through this on Apple's own forums got a 200 back from the APNs API on every request and still saw nothing happen on the device, and an Apple DTS engineer confirmed the reason directly: pass update pushes only get delivered through the production APNs environment. The sandbox environment doesn't deliver this notification type, full stop, regardless of what the API response claims.

It's an easy mistake to make, because most push integrations treat sandbox and production as interchangeable for testing and only switch over at release. Wallet updates don't allow that. If you're using a library like node-apn, the fix is one flag:

import apn from 'apn'

const apnProvider = new apn.Provider({
  token: { key, keyId, teamId },
  production: true, // required for Wallet pass updates, sandbox never delivers them
})

const notification = new apn.Notification()
notification.topic = passTypeIdentifier
notification.contentAvailable = true
// payload stays empty, Wallet only needs to know something changed

await apnProvider.send(notification, devicePushToken)

If you've been testing against sandbox and wondering why nothing ever arrives, this is almost certainly why, and it's worth ruling out before you touch anything else.

The certificate and the topic have to match the pass, not your app

Two fields on the push have to point at the pass, not at your app's own notification setup, and both fail the same way: APNs accepts the request, and Wallet quietly ignores it. apns-topic has to be the pass type identifier your pass was issued under, such as pass.com.example.loyalty, never your app's bundle ID. And the push has to be signed with the same Pass Type ID certificate that signed the pass, not a separate APNs key or certificate your app already uses to send its own users notifications. If your team has one shared push setup for everything, this is the field most likely to get set once and forgotten.

Two more headers matter less often but still trip people up. apns-push-type has to be background, and apns-priority has to be 5, the value Apple reserves for pushes with no user-visible alert. Send a Wallet update at priority 10 and some devices will drop it as malformed rather than deliver it as a silent wake-up.

A tag that never moves is the same as no update

Once a device wakes up from a valid push, it asks your server which passes changed by calling getSerialNumbers with the last tag it saw. Your response has to include a fresh lastUpdated tag alongside the serial numbers that changed since that tag, something like:

{
  "lastUpdated": "1758758400",
  "serialNumbers": ["AA1180-2026-09-16"]
}

If your server keeps returning the same tag it handed out last time, or returns 204 because the comparison logic never recognized the field that actually changed, the device is being told, correctly by the letter of the protocol, that nothing is different. It stops right there. It never asks for the pass itself, so a bug at this one endpoint looks identical from the outside to a push that never arrived at all.

The same silent failure shows up one step further along. A developer troubleshooting exactly this symptom on Apple's forums traced it to their /v1/passes/{passTypeIdentifier}/{serialNumber} endpoint returning a 405 on HEAD requests, since their code only handled GET. Wallet sends a HEAD first in some circumstances, and a method-not-allowed response there stops the update before the actual pass data ever gets requested. If your tag logic checks out, confirm your pass endpoint answers HEAD with the same headers, Content-Type, Content-Disposition, Last-Modified, that it would send with the real response.

What's a device problem, not yours

Once registration, environment, certificate, topic, and tag logic all check out, what's left is mostly outside your server's control, and it's worth recognizing so you stop debugging code that isn't broken. A silent push carries no user-visible alert, and iOS treats it as the first thing to defer under battery pressure. Low Power Mode is the most common version of this, and a device with no network connection at the moment of the push simply has nowhere to deliver it until connectivity returns. Some developers also report Wallet's own daemon growing slower to act on further pushes after several land for the same pass in quick succession, though Apple hasn't documented a rate limit to confirm it, so treat that one as a possibility worth ruling out last, not a first assumption.

The practical difference is what happens on retry. A device-side delay resolves itself: the pass updates late, with no code change on your end, the moment Low Power Mode turns off or the phone reconnects. A server-side bug doesn't resolve itself no matter how many times the same push gets sent. If a stuck pass update always clears up eventually with no server change, you're looking at the device. If it never does, go back to the top of this list.

Passmint handles device registration, the push, and the tag comparison as one call, so a dropped HEAD handler or a stale lastUpdated value never gets the chance to become a four-time repeat complaint at your support desk.

Primary sources

Common questions

APNs confirms it delivered the push to the device, not that Wallet did anything with it. A wrong topic, a push signed with the wrong certificate, a stale update tag, or a push sent to the sandbox environment all return success from APNs while the pass on the phone never changes.
No. A push for a pass update only gets delivered through the production APNs environment. Apple engineers have confirmed on the developer forums that the sandbox environment does not deliver this notification type at all, even when the push itself reports success.
No. The push has to be signed with the same Pass Type ID certificate that signed the pass itself, sent with that identifier as the apns-topic. A push signed with a separate APNs key for your app is accepted by APNs and ignored by Wallet.
Usually a device-side condition, not a server bug. Low Power Mode and a lost network connection both delay a low-priority silent push, and the update lands as soon as the condition clears. If it never lands, the cause is on the server, not the device.
Julio Song

Technical content writer, Passmint

Julio is a technical content writer at Passmint. He writes about Apple PassKit, the Google Wallet API, and what breaks when wallet passes meet production traffic.

More from Julio Song →

Related reading

Share this article