WalletWallet API
Get API Key Docs Pricing Changelog Log in
Back to Blog

One API, Two Wallets: Google Wallet Support Is Live

The same pass request now returns both an Apple .pkpass file and a Save to Google Wallet link. How the cross-platform request, response, button, and update path work.

2026-06-06 By Alen google-wallet apple-wallet passes api announcement

Google Wallet support is live. The request you already send to create an Apple pass now also returns a Save to Google Wallet link. There is no second integration to write and no second pass model to learn: one request body, validated once, produces a signed .pkpass file for Apple and a save link for Google.

An Apple Wallet pass and a Google Wallet pass generated from the same request

One request to /api/passes produces both of these. The Apple Wallet pass on the left and the Google Wallet pass on the right share the same logo, fields, color, and barcode.

Updates stay just as unified. A single PUT refreshes the pass on whichever wallet a user installed it in, with the platform differences handled for you.

One request, both wallets

You describe a pass with a neutral JSON body. The same body that produced an Apple pass yesterday now drives both platforms. To get the cross-platform response, POST to /api/passes:

curl -X POST https://api.walletwallet.dev/api/passes \
  -H "Authorization: Bearer ww_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Bayroast Coffee",
    "colorPreset": "dark",
    "logoText": "Bayroast Coffee",
    "barcodeFormat": "QR",
    "barcodeValue": "MEMBER-4821",
    "primaryFields": [{ "label": "Member", "value": "Ada L." }],
    "secondaryFields": [{ "label": "Stamps", "value": "3 / 10" }]
  }'

The response is a JSON envelope with everything you need to put the pass on either phone:

{
  "serialNumber": "b9c1f2e4-...",
  "googleSaveUrl": "https://pay.google.com/gp/v/save/eyJhbGci...",
  "applePass": "UEsDBBQACAgI...",
  "shareUrl": "https://api.walletwallet.dev/p/b9c1f2e4-..."
}

applePass is the base64 of the signed .pkpass. googleSaveUrl is a signed link to Google Wallet. shareUrl is a hosted page that offers both. The barcode, fields, and color come from the one request, so the two cards carry the same data and the same look.

The envelope hands you every artifact at once, so you are not locked into one flow. Drop the applePass bytes into your own download, render your own Save to Google Wallet button from googleSaveUrl, send the shareUrl, or branch on the device in front of you. If you have an unusual surface, a kiosk, an email, an in-app step, you have the pieces to build exactly the flow it needs.

Your existing .pkpass calls are unchanged

POST /api/pkpass still returns the raw .pkpass binary as its body, exactly as before. Nothing about that response moved. The Google link rides along on a response header instead, so a client that reads the body for the file and ignores headers behaves identically to how it did last week:

curl -X POST https://api.walletwallet.dev/api/pkpass \
  -H "Authorization: Bearer ww_live_..." \
  -H "Content-Type: application/json" \
  -d @pass.json \
  -D headers.txt \
  -o member.pkpass

grep -i x-google-save-url headers.txt
# x-google-save-url: https://pay.google.com/gp/v/save/eyJhbGci...

If you want the JSON envelope from this route too, add ?format=json. The two routes share one handler and differ only in their default response, so /api/pkpass?format=json and /api/passes return the same shape.

The Save to Google Wallet button

You can hand the googleSaveUrl straight to a user, but you do not have to hold onto it. Each pass also has a public redirect keyed by its serial:

<a href="https://api.walletwallet.dev/api/passes/b9c1f2e4-.../google">
  Save to Google Wallet
</a>

GET /api/passes/{serial}/google responds with a 302 to the current save URL. It takes no Authorization header, because the unguessable serial is itself the capability, the same model the .pkpass download already uses. That means the button is a plain link with no token handling and no client-side JavaScript. Put it next to your existing Apple download and the hosted shareUrl page does exactly this with both buttons.

The share URL: one branded page, either wallet

The shareUrl in the response is a hosted page for the pass, already branded with the pass logo and color, that presents both the Apple and the Google option. You hand a customer one link and they pick the wallet they have, without you detecting their device or hosting anything yourself:

https://api.walletwallet.dev/p/b9c1f2e4-...

This is the link to drop into a confirmation email, a text message, or a “Add to your wallet” button on your own site when you would rather not build the two-button UI. The page reads the pass by its serial and shows the right action on either platform.

The hosted share page showing a QR code with an Add to Apple Wallet and an Add to Google Wallet button

The hosted shareUrl page, branded with the pass logo, a QR code to scan from a phone, and a button for each wallet.

Built for visual parity

This release took its time on purpose. Apple and Google model passes differently, render them differently, and adjust colors and images under their own rules, so a single design will never land pixel for pixel on both. The work here was finding the middle ground where a pass looks like the same pass on either phone: the color presets resolve to the same values, the logo and fields land in matching regions, and the result holds up on both rather than looking right on one and compromised on the other.

A Google Wallet loyalty pass rendered on Android

The same pass in Google Wallet. Google stacks the fields and centers the barcode in the card body, and it rounds the logo into a circle, so the builder maps your request onto that layout rather than forcing Apple’s.

You can tune and preview both renderings live on the homepage editor before you make a single API call.

One update path for both platforms

When the pass changes, send the new contents to PUT /api/pkpass/{serial} with the serial you got at creation:

curl -X PUT https://api.walletwallet.dev/api/pkpass/b9c1f2e4-... \
  -H "Authorization: Bearer ww_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Bayroast Coffee",
    "colorPreset": "dark",
    "logoText": "Bayroast Coffee",
    "barcodeFormat": "QR",
    "barcodeValue": "MEMBER-4821",
    "primaryFields": [{ "label": "Member", "value": "Ada L." }],
    "secondaryFields": [{ "label": "Stamps", "value": "4 / 10" }]
  }'

PUT replaces the pass contents rather than patching them, so send the full body you want the pass to hold. That one call propagates to both platforms, so you write the pass once and update it once, regardless of which wallet a given user chose.

Where the platforms still differ

The differences that remain are the ones inherent to the platforms. Apple passes are signed files that live on the device; Google passes are records that live in Google’s database and render from it. You do not manage those differences yourself; both platforms sit behind the same POST and PUT.

Start issuing

Get an API key from the dashboard, send one POST /api/passes, and you have both a .pkpass and a Save to Google Wallet link from a single request. For the field-by-field breakdown of each platform, see the anatomy guides for Apple Wallet and Google Wallet passes.

Build your first wallet pass

Turn one JSON request into a pass that installs in Apple Wallet and Google Wallet, with live updates that reach both.