Powered by Cloudflare Workers · instant push updates · 200ms pass generation worldwide
WalletWallet API
Get API Key Docs Pricing Changelog Log in

The simplest Wallet pass API.

Create a pass with one request, update every installed device with the next. No certificates. JSON in, Apple and Google passes out.

Free up to 1,000 passes/month, then $0.00019 per pass on Pro.

An alternative to PassKit, Pass2U,
or building on Apple's and Google's SDKs.

Issue passes once, update them anytime.

Every pass gets a serial number. Call it again to update the pass or send a notification to the customer's lock screen.

  1. Issue the pass

    POST the pass content. You get back JSON with the .pkpass, a Save to Google Wallet link, and its serial number. The customer adds it to Apple or Google Wallet.

    POST /api/passes
    curl -X POST https://api.walletwallet.dev/api/passes \
      -H "Authorization: Bearer ww_live_<key>" \
      -H "Content-Type: application/json" \
      -d '{
        "barcodeValue": "LOYALTY-98765",
        "barcodeFormat": "QR",
        "logoText": "Bayroast Coffee",
        "headerFields": [{ "label": "POINTS", "value": "1,250" }],
        "primaryFields": [{ "label": "CARD", "value": "Coffee Rewards" }]
      }'
  2. Push updates

    PUT the updated body to that serial. The pass refreshes on every device it's installed on, across Apple Wallet and Google Wallet, and a notification fires.

    PUT /api/passes/<serial>
    curl -X PUT https://api.walletwallet.dev/api/passes/<serial> \
      -H "Authorization: Bearer ww_live_<key>" \
      -H "Content-Type: application/json" \
      -d '{
        "barcodeValue": "LOYALTY-98765",
        "barcodeFormat": "QR",
        "logoText": "Bayroast Coffee",
        "primaryFields": [{ "label": "CARD", "value": "Coffee Rewards" }],
        "headerFields": [{ "label": "POINTS", "value": "1,300", "changeMessage": "You hit %@ points. Free coffee unlocked!" }]
      }'
  3. Surface it by location

    Attach coordinates to the serial. Apple pins the pass to the lock screen when the customer is near the place you set.

    PUT /api/passes/<serial>
    curl -X PUT https://api.walletwallet.dev/api/passes/<serial> \
      -H "Authorization: Bearer ww_live_<key>" \
      -H "Content-Type: application/json" \
      -d '{
        "barcodeValue": "LOYALTY-98765",
        "barcodeFormat": "QR",
        "logoText": "Bayroast Coffee",
        "primaryFields": [{ "label": "CARD", "value": "Coffee Rewards" }],
        "locations": [{ "latitude": 40.7484, "longitude": -73.9857, "relevantText": "Free refill at our Empire State store" }]
      }'

No certificate to manage or APNs to setup. You only need to store the serial.

Share a pass with one link.

Every pass you create comes with its own hosted page. Send the link however you already reach people, email, SMS, or an "Add to Wallet" button, and it shows the right wallet for whoever opens it. No app to install, nothing to host yourself.

On iPhone

An Add to Apple Wallet button drops the signed pass straight into Apple Wallet.

On Android

A Save to Google Wallet button adds the same pass to Google Wallet.

On desktop

A QR code appears so the visitor can scan it and add the pass from their phone.

The page carries your logo and color, so it looks like your pass, not a generic install screen.

Prefer to build your own? The create response also returns the pass itself, the signed .pkpass and the Save to Google Wallet link, so you can host your own share page instead.

Why WalletWallet?

A unique serial per pass

Call it any time after issue to update the pass and push a notification. One call reaches every device the customer installed it on: iPhone, iPad, Apple Watch, and Android.

Live pass updates

PUT new fields to a serial. The pass refreshes on every device it installed on, across Apple Wallet and Google Wallet.

Push notifications

Handled automatically on both wallets: APNs for Apple, server push for Google. Apple shows your changeMessage on the lock screen; Google shows a generic update notice.

Your branding

Logo, thumbnail, strip banner, foreground and background colors, custom field labels.

Lock-screen locations

Up to 10 GPS coordinates per pass. Wallet shows the pass when the user is nearby.

Edge-deployed

Hosted on Cloudflare Workers. Sub-200ms pass generation worldwide.

Preview the pass before any API call.

Tune every field, color, and image and watch the pass update. Copy the code when you're done.

Text next to the logo (top-left)

Notification title on pass updates. Defaults to your account name.

No header fields

No primary fields

No secondary fields

No back fields

Click the pin to fill a row with where you are, for testing. The real pass needs the actual place.

Wallet shows the pass on the lock screen within ~100m of a coordinate.

Pro feature — upgrade to add up to 10 lock-screen location triggers per pass.

Overrides color preset

Lock-screen and pass-update notification image. Defaults to your logo.

Wide banner image. Use secondary fields for readable text when using this option.

EXPIRES

On Google, your strip image shows beneath the QR code, and header fields sit in the field row, not the top-right corner.

The full API. Two endpoints.

Bearer auth, JSON in, signed .pkpass out. Click any endpoint to expand the schema.

Auth Authorization: Bearer ww_live_<your_key> Base https://api.walletwallet.dev
POST /api/passes

Request body

Field Type Req Description
barcodeValue string Yes Data encoded in the barcode (e.g., member ID, ticket number).
barcodeFormat string Yes One of QR PDF417 Aztec Code128.
logoText string No Text next to the logo (top-left of pass).
description string No Accessibility text (not visible). Defaults to logoText.
organizationName string No Issuer name shown as the notification title on pass updates and in the Wallet info screen. Max 64 chars. Falls back to your account default.
primaryFields array No Main content. Array of {label, value, changeMessage?}. changeMessage is the lock-screen banner template that fires when this field changes during a PUT.
secondaryFields array No Fields below primary. Array of {label, value, changeMessage?}.
headerFields array No Top-right header area. Array of {label, value, changeMessage?}.
backFields array No Back of pass. Array of {label, value, changeMessage?}.
locations array No Up to 10 geofences. {latitude, longitude, altitude?, relevantText?} per entry. Surfaces the pass on the lock screen when the device is nearby.
sharingProhibited boolean No Hides the Apple Wallet share button. Defaults to true (best for loyalty and membership cards). Set false to let holders share the pass.
colorPreset string No Color theme: dark blue green red purple orange.
color string No Custom hex color (Pro). e.g., #1e40af.
logoURL string No Custom logo image (Pro). HTTPS URL or PNG data URI.
thumbnailURL string No Top-right image (Pro). HTTPS URL or PNG data URI.
stripURL string No Banner behind the primary field (Pro). Switches to store-card layout. HTTPS URL or PNG data URI.
iconURL string No Replaces the default lock-screen notification icon (Pro). Distinct from logoURL. HTTPS URL or PNG data URI.
title string No Legacy. Sets primaryFields[0].value + logoText if unset.
cardLabel string No Legacy. Sets primaryFields[0].label. Defaults to CARD.
label string No Legacy. Sets secondaryFields[0].label.
value string No Legacy. Sets secondaryFields[0].value.
expirationDays number No Pass expires after this many days. Any integer between 1 and 3650.

Response

  • 200 JSON: { serialNumber, googleSaveUrl, applePass, shareUrl }. applePass is the base64 .pkpass; googleSaveUrl is the Add to Google Wallet link; shareUrl is a hosted install page you can send straight to users.
  • 400 Invalid request body or missing required fields.
  • 401 Invalid or missing API key.
  • 429 Rate limit exceeded.
  • 500 Server error.

Example

curl -X POST https://api.walletwallet.dev/api/passes \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ww_live_<your_key>" \
  -d '{
    "barcodeValue": "MEMBER-12345",
    "barcodeFormat": "QR",
    "logoText": "Bayroast Coffee",
    "primaryFields": [{"label": "CARD", "value": "Coffee Rewards"}],
    "colorPreset": "green"
  }'
Full POST reference →
PUT /api/passes/<serial>

Request body

Same shape as POST: send the full pass body. The new body replaces the stored pass, so include every field you want to keep — omitted fields are dropped, not merged. Identical bodies are no-ops: no APNs push, no quota cost. Setting changeMessage on a field whose value changed sets the lock-screen banner text.

Response

  • 200 JSON: { serialNumber, lastUpdated, notifiedDevices, unchanged }. A changed body re-signs the pass and pushes to every installed device; an identical body returns unchanged: true with no push and no quota cost.
  • 400 Invalid request body.
  • 401 Invalid or missing API key.
  • 404 Serial not found.
  • 429 Rate limit exceeded.

Example

curl -X PUT https://api.walletwallet.dev/api/passes/<serial> \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ww_live_<your_key>" \
  -d '{
    "headerFields": [{
      "label": "POINTS",
      "value": "1,300",
      "changeMessage": "Now at %@ points"
    }]
  }'
Full PUT reference →

Coding with Codex, Claude, or another agent? Point it at /llms.txt for tight, up-to-date docs.

Flat pricing. No per-pass fees, no per-device fees.

For reference, PassKit charges $1,300/month for the same pass lifecycle features.

Free

Testing & side projects

$0 /month
  • 1,000 passes/month
  • Limited branding
  • Signed .pkpass delivery
Start free
30-day Pro trial

Pro

Production apps

$19 /month
  • 100,000 passes/month
  • Live updates & push on both wallets
  • Lock-screen location triggers
  • Logo, thumbnail, strip, icon
  • Priority email support
Start 30-day trial

Bring your own cert

Sign with your Apple Developer ID

Custom
  • Sign with your Apple certificate
  • Managed APNs on dedicated, low-latency workers
  • Volume pricing past 100k passes/month
  • SLA & dedicated technical support
  • Custom contract, DPA & onboarding
Schedule a call

I built WalletWallet because I needed Wallet passes for one of my apps, and the options were a $40/month dashboard or a week waiting on Apple for a signing certificate. Now it's one API call, signed with our cert, $19 flat. My email is on every page. I read all of it.

Alen, founder

Cool things you can build.

  • WalletWallet For agencies

    Run an agency? Sell a lightweight CRM to your small business clients.

    Build a white-label notification CRM on our API. Your agency clients pay you per customer they reach. We charge you $19 flat, no matter how many customers move through it.

  • WalletWallet For event organizers

    Host events? Make the badge update itself.

    Hand every attendee a pass that lives next to their physical badge. Push session changes, room swaps, and tomorrow’s agenda straight to their lock screen.

  • ShopifyWoo For store owners

    Running a small store? Skip the app build.

    Drop a pass into every order confirmation. Ping customers on the lock screen for restocks, drops, and order updates. No app install.

  • WalletWallet For B2B SaaS

    Building a B2B SaaS? Ship Wallet passes for every client.

    Your clients are businesses with their own customers to reach. Integrate WalletWallet once and the Wallet pass feature ships across every account on your platform. We don’t bill per client.

Build passes from the tools you already use

  • SumUp
  • Shopify
  • Woo
  • n8n
  • Zapier

Frequently asked questions

What's in the 30-day Pro trial?

Every new account starts on Pro for 30 days: all features, all image slots, lifecycle updates, unlimited passes. After that, keep Pro at $19/month or stay free under 1,000 passes/month. No card up front, no auto-charge.

Does this work with Google Wallet too?

Yes, fully. One API call lands a pass in both wallets: Apple installs the signed .pkpass, and Google installs from a Save to Google Wallet link we generate for the same pass. Live updates and push work on both too. We re-sign and push to Apple over APNs, and update the Google object and push directly. One honest difference: Apple shows your custom message on the lock screen, while Google's update banner is generic and your text lives inside the pass.

Can I update a pass after it's been added to someone's wallet?

Yes. Every pass gets a unique serial, returned as serialNumber in the JSON response and embedded inside the .pkpass file. PUT a new body to /api/passes/<serial> and the pass refreshes on every device it was added to, iPhone, iPad, Apple Watch, and Android. Identical bodies don't trigger a push and don't count against your quota.

How do I send the pass to my customers?

The API response carries the signed .pkpass (base64 applePass) and a Save to Google Wallet link for the same pass. Forward them however you already reach your user: email, an "Add to Wallet" button on a confirmation page, SMS, or in-app. Or share the hosted pass page we host at /p/<serial>, which shows the right Add button for the visitor's phone, Apple on iPhone, Google on Android.

Issue your first pass in five minutes.

Free under 1,000 passes a month. $19 flat above that, with 30 days of Pro on every new account.

curl -X POST https://api.walletwallet.dev/api/passes …