Skip to main content

Wallet passes

Orbit can issue digital passes for Apple Wallet and Google Wallet: loyalty cards, coupons, and event tickets your customers save to the wallet app on their phone. Issue them through the API or from the dashboard’s issuance builder, and deliver them by putting the pass’s save link in any message you send — SMS, WhatsApp, or email. Three pass types are supported today:
  • Loyalty cards — points balance, tier, or membership number behind a scannable barcode.
  • Coupons — an offer with a redemption barcode and an optional expiry date.
  • Event tickets — a ticket with seat, gate, or order details.

The pass lifecycle

Every pass moves through the same lifecycle:
  1. IssuePOST /wallet-passes/issue creates the pass and returns both platform payloads (an Apple pass.json structure and a Google “Save to Wallet” link). New passes start active.
  2. UpdatePOST /wallet-passes/:id/update patches an active pass’s content (points balance, tier, coupon expiry, ticket seat). Each accepted update bumps the pass’s generation counter, which is the signal that the holder should refresh the pass on their phone.
  3. VoidPOST /wallet-passes/:id/void permanently revokes an active pass. Voiding is terminal: a voided pass reports status: "voided" and rejects any further update or re-void with a 409 CONFLICT error.
Request body (issue):

Issuing a pass

Response

Issuing is idempotent: pass an idempotency_key (or an Idempotency-Key header) and a retry returns the original pass with replayed: true instead of minting a second one. The replayed response reflects the pass’s current state — so a delayed retry after an update or void never reports stale content.

Platform status

Before issuing, check which platforms are live:
  • appleconfigured: true when the Apple Wallet pass-type id and team id are connected. Until then pass_json is null, because there is no valid certificate identity to stamp the pass with.
  • googleconfigured: true when the Google Wallet issuer id and service-account credentials are connected. Until then save_url is null.
  • types / barcode_formats — the enums the issuance form can offer; use these to drive your own UI validation.
You can issue and manage passes with no platform connected — the pass content is stored and readable — but the downloadable payloads appear only after the matching platform is connected. Every read rebuilds the platform payloads fresh, so existing passes start producing working save links the moment credentials are connected; you never need to re-issue. Each platform has its own signing mechanism:
  • Apple Wallet builds the pass as a signed .pkpass bundle: a PKCS#7 signature over a manifest of the pass files, using the Apple-issued pass signing certificate tied to your pass type. Orbit generates the unsigned pass.json content (platforms.apple.pass_json); the final binary signing step runs on the transport that holds the certificate material, once the Apple credentials are connected.
  • Google Wallet builds a signed “Save to Google Wallet” link (platforms.google.save_url): the pass is wrapped in a Google generic object and encoded as an RS256-signed token appended to https://pay.google.com/gp/v/save/.... Opening that link on an Android device or in a browser adds the pass to the holder’s Google Wallet account.

Listing, updating, and voiding passes

List issued passes (newest first) with optional filters:
Update an active pass’s content — only the fields you send change; everything else carries forward:
Each update increments generation. To clear a field, send it as explicit null. Void a pass (optionally with a reason):
A voided pass keeps its content for audit, reports status: "voided", renders as expired in the wallet, and cannot be updated. The most common onboarding flow: a customer joins your loyalty program, and you text them their wallet pass.
  1. Issue the pass tied to the enrolling contact:
  2. Pick the save link from the response — data.platforms.google.save_url for Google Wallet holders (the Apple Wallet equivalent is the signed pass the .pkpass transport serves once Apple credentials are connected).
  3. Send the link over SMS using the standard messaging endpoint:
    Keep the message brand-clean — save links are long, so route them through Orbit link shortening if you want a short URL. The same deep-link pattern works on SMS, WhatsApp, or email; pick whichever channel the customer opted into.

The dashboard issuance builder

Operators can run the whole lifecycle without the API. In the dashboard, open Marketing → Wallet passes (also reachable at Outbound → Wallet passes). The surface is gated to owner, admin, and developer roles.
  • Issue a pass — the New pass form offers exactly the pass types and barcode formats GET /wallet-passes/platforms reports, and flags when a platform is not yet connected.
  • Update content — edit points, tiers, or other fields on an active pass; each accepted edit bumps the pass generation.
  • Void a pass — revoke through a confirmation dialog; voided passes remain visible in the list for audit.
  • Copy save links — copy the Google save link (or read the Apple payload status) to drop into a message.
The dashboard surface and the API operate on one shared ledger, so a pass issued from the dashboard is visible to the API and vice versa.

How issuance state is stored

Wallet-pass state lives as events in the same append-only customer-data ledger that powers loyalty programs and incentive issuance. Issuing appends an issuance event; updates and voids append overlay events; and the current state of a pass is folded from its event stream on every read. There is no separate wallet-passes table to migrate, and nothing external to keep in sync — which is also what makes idempotent replays safe: the replayed response folds to the pass’s current lifecycle state. All wallet-pass endpoints are scoped with contacts:read (list/get/platforms) and contacts:write (issue/update/void) — a pass is customer-facing collateral tied to a contact.

Common errors