Passes

A pass is one instance of a template for one end-user โ€” a customer's loyalty card, an attendee's event ticket, a traveller's boarding pass.

Intents

The intent field determines how the worker maps your data to Apple Wallet and Google Wallet object types.

intentGoogle TypeApple PassStyle
genericGenericObjectGeneric
loyalty_cardLoyaltyObjectStoreCard
couponOfferObjectCoupon
event_ticketEventTicketObjectEventTicket
boarding_passFlightObjectBoardingPass

Create a Pass

POST /api/v1/passes

Returns 202 Accepted. The pass is created asynchronously per provider.

Headers

HeaderDescription
AuthorizationrequiredBearer {token}
Idempotency-KeyoptionalAny client-generated key. Same key + tenant returns the original pass.

Body

FieldTypeDescription
template_idUUID required Must reference a Template the tenant owns or a system template.
external_refstring required Your customer-side id. Unique per (tenant, external_ref); idempotent re-POST.
intentenum optional Defaults to generic. Must match the template's template_type (Generic is the wildcard).
dataobject optional Free-form key/value bag the worker uses to fill template placeholders.
metaobject optional Pass-through metadata. Stored on the pass; not sent upstream by default.
google_grouping_idstring optional Google groupingInfo.groupingId (max 64). Groups passes โ€” even of different types โ€” in Google Wallet. Wins over the id derived from linked_to_external_ref. On update, an empty string "" removes the grouping.
google_sort_indexinteger optional Google groupingInfo.sortIndex (≥ 0). Only takes effect together with a google_grouping_id.
apple_grouping_identifierstring optional Apple groupingIdentifier (max 64). On update, "" removes it. Applied ONLY to event_ticket and boarding_pass passes (see note).
Grouping (v3-138): the three fields above are the only way to group passes โ€” there is no UI for it. Apple groups only tickets and boarding passes โ€” an apple_grouping_identifier supplied for any other pass style is ignored, and the response carries a warnings entry to say so. Google groups all pass types via google_grouping_id. All three fields are also accepted on PATCH (update) and on PUT /passes/by-external-ref/{externalRef}; an empty string clears the stored value.

When it reaches the wallet: grouping is written to the Google object and the Apple pass at creation. An update that sets or changes a Google grouping field (google_grouping_id/google_sort_index) now also pushes groupingInfo to the already-issued Google object (v3-139). Removing a Google grouping (google_grouping_id: "") is applied via the update and takes effect on the object (device-verified); Google does not formally guarantee it, so a warnings entry is returned. A plain data-only update and auto-updates/template-updates never touch groupingInfo (an existing grouping is never lost). Apple applies apple_grouping_identifier only when the pass is added to Wallet: an update reaches already-installed tickets/boarding passes but does not regroup them until the pass is removed and added again โ€” so an update that changes apple_grouping_identifier on a ticket/boarding pass returns a warnings entry saying so.

Linked passes vs. grouping (v3-139): a pass created with linked_to_external_ref is an auto-linked pass โ€” it appears inside the primary's stack in Google Wallet, and if the primary is already saved on the device Google delivers the linked pass automatically (best effort, possibly delayed; the user can turn this off). The link (linkedObjectIds) never overwrites a grouping: the linked secondary inherits the primary's effective groupingId (unless it supplies its own), and the primary keeps its own. Apple has no equivalent โ€” linking is Google-only.

Apple auto-stacking: Apple stacks coupons and store cards automatically by their certificate (passTypeIdentifier), independently of apple_grouping_identifier (which only affects event tickets and boarding passes). Passes issued under the same Apple certificate may therefore appear stacked on the device even without an explicit grouping.
GroupingGoogleApple
On create All pass types, also mixed (google_grouping_id). Only event tickets & boarding passes (apple_grouping_identifier).
Change / remove Applied immediately via the update (device-verified). Reaches installed passes, but regroups only after the pass is removed & re-added.
Coupons / store cards Grouped by google_grouping_id like any type. Auto-stacked by certificate (passTypeIdentifier); grouping identifier has no effect.
Linking (linked_to_external_ref) Google-only; also after the fact โ€” the linked pass appears automatically in the primary's stack (best effort). No equivalent.

Example

POST /api/v1/passes
{
  "template_id":  "3f2a1b4c-...",
  "intent":       "loyalty_card",
  "external_ref": "cust-42",
  "data": {
    "member_name": "Maria Schmidt",
    "member_id":   "MBR-042",
    "points":      "2400"
  }
}
โ— 202 Accepted
{
  "id":           "57725a27-...",
  "tenant_id":    "...",
  "template_id":  "3f2a1b4c-...",
  "external_ref": "cust-42",
  "status":       "pending",
  "version":      1,
  "providers": {
    "google": { "status": "queued" },
    "apple":  { "status": "queued" }
  },
  "job_id":       "...",
  "created_at":   "2026-05-18T10:00:00Z"
}

Get a Pass

GET /api/v1/passes/{id}

The response includes per-provider state โ€” poll this endpoint until status stabilises.

Pass status values

statusMeaning
pendingNo provider has completed yet.
activeEvery provider job is completed.
partialAt least one provider completed, others still in-flight or failed.
failedAll provider jobs failed terminally.
updatingAn UpdatePass job is in-flight.
revokedThe pass has been revoked.
expiredTime-based expiry reached.
deletedSoft-deleted; hidden from list/get.

Provider status object

"providers": {
  "google": {
    "status":     "completed",
    "save_link":  "https://pay.google.com/gp/v/save/...",
    "object_id":  "3388000000023021962.tenant1234_cust42"
  },
  "apple": {
    "status":        "completed",
    "download_url":  "https://api.walletplatform.io/api/v1/passes/57725a27-.../pkpass",
    "serial_number": "57725a27-2093-42d0-8cb9-d4f1a5e9b3c7"
  }
}

List Passes

GET /api/v1/passes?page=1&page_size=20&status=active&external_ref=cust-42

Update a Pass

PATCH /api/v1/passes/{id}

Updates the pass data. Each active provider gets its own UpdatePass job; Apple devices receive an APNs push notification so they pull the new .pkpass on the next sync.

Revoke a Pass

DELETE /api/v1/passes/{id}

Marks the pass revoked and notifies every active provider. Apple devices flip the pass to expired on the next pull; Google's upstream object is patched to EXPIRED.

Version history

GET /api/v1/passes/{id}/versions

Returns every version row (Create / Update / Revoke), newest first.

Download pkpass

GET /api/v1/passes/{id}/pkpass

Apple-specific. Authenticates via the per-pass auth token in Authorization: ApplePass {token} (the same token embedded in the .pkpass file). Returns application/vnd.apple.pkpass.

For a full request/response reference, open the API reference.