This guide is a short reference for the five pass types wallet_passes
supports, the platform-specific resources each one maps to, and the
PassData fields that are meaningful per type. The library exposes pass
type as a single atom on PassData.pass_type and threads it through both
the Apple .pkpass builder and the Google Wallet API client.
Forward links:
- Getting Started for a full first-pass walkthrough.
- Apple Wallet for the Apple style-key behaviour.
- Google Wallet for class/object semantics.
Overview
A "pass type" describes the shape of the pass: an event ticket renders
differently from a boarding pass, and a loyalty card differently again.
Both Apple and Google model this, but they expose it in different shapes
of their API. This library normalises both behind a single atom on
PassData:
%WalletPasses.PassData{pass_type: :event_ticket, ...}The default is :event_ticket. The five supported atoms are
:event_ticket, :boarding_pass, :store_card, :coupon, and
:generic (note: :store_card, not :loyalty — see the table below).
Concepts
Apple: a top-level "style key" inside pass.json
Apple's pass.json carries exactly one of eventTicket, boardingPass,
storeCard, coupon, or generic as a top-level key. Whichever key is
present determines how iOS lays out the pass; the value is the
"pass-structure dictionary" that holds the field arrays (headerFields,
primaryFields, secondaryFields, auxiliaryFields, backFields) and,
for boarding passes only, the transitType. This library picks the
correct top-level key from pass_data.pass_type.
Google: a different resource per type
Google Wallet's REST API exposes five different resource families. Each
pass type has its own class endpoint, its own object endpoint, and its
own JWT payload key for the Save-to-Wallet link. There is no single
"pass" endpoint; you must hit the right resource for the type you want.
This library resolves all three from pass_data.pass_type via
WalletPasses.PassType so callers don't have to think about it.
Type Mapping
pass_type | Apple style key | Google class | Google object | Save-URL JWT key | Class ID suffix |
|---|---|---|---|---|---|
:event_ticket | eventTicket | eventTicketClass | eventTicketObject | eventTicketObjects | event_class |
:boarding_pass | boardingPass | flightClass | flightObject | flightObjects | flight_class |
:store_card | storeCard | loyaltyClass | loyaltyObject | loyaltyObjects | loyalty_class |
:coupon | coupon | offerClass | offerObject | offerObjects | offer_class |
:generic | generic | genericClass | genericObject | genericObjects | generic_class |
Two naming asymmetries to note:
- Apple calls a loyalty pass a "store card" (
storeCard); Google calls it "loyalty" (loyaltyClass/loyaltyObject). This library uses the atom:store_card— pick whichever name you prefer, but the atom is:store_card. - Apple calls flight/transit passes "boarding pass"; Google calls the
same resource
flightClass/flightObject. The library accepts:boarding_passfor both.
Field Applicability
Most PassData fields apply to every pass type. The field arrays
(header_fields, primary_fields, secondary_fields,
auxiliary_fields, back_fields) are universal — they always populate
the corresponding Apple pass-structure section, and on Google they
flatten into textModulesData entries (Google doesn't have separate
"primary/secondary" zones in the same way).
The fields below are either type-conditional or get mapped to different Google object fields depending on type:
PassData field | :event_ticket | :boarding_pass | :store_card | :coupon | :generic |
|---|---|---|---|---|---|
holder_name | Google: ticketHolderName | Google: passengerName | Google: accountName | not mapped | Google: header.defaultValue (top-line text) |
transit_type | ignored | required (defaults :air) | ignored | ignored | ignored |
event_name | suggested for class title | suggested for class title | suggested for class title | suggested for class title | suggested for class title |
A few notes on the above:
holder_namemapping. On Apple,holder_nameisn't emitted directly intopass.json— surface it through your fields (e.g. a primary or secondary field) if you want it visible. On Google, the library writes it into a type-appropriate Google object property as shown.event_nameis aPassDatafield, but Apple doesn't read it. It's intended as data yourPassDataProvidercarries through to the Google class builder, where it maps to the type-specific class title field (see "Class-shape differences" below). On Apple, set the visible title throughApple.Visual.logo_textanddescription.- NFC fields, location fields, dates, and barcode fields are type-independent. They apply identically to every pass type. See the NFC & Smart Tap guide for NFC specifics.
transit_type for :boarding_pass
When pass_type: :boarding_pass, the Apple builder writes a
transitType key into the pass-structure dictionary. The atom on
PassData.transit_type maps to Apple's PKTransitType* enum:
transit_type atom | Apple transitType value |
|---|---|
:air | PKTransitTypeAir |
:boat | PKTransitTypeBoat |
:bus | PKTransitTypeBus |
:train | PKTransitTypeTrain |
:generic | PKTransitTypeGeneric |
nil defaults to :air. The field is ignored for every pass type other
than :boarding_pass — setting it on a :store_card won't produce
transitType in the output. Google's flightClass/flightObject
resources don't take an equivalent enum; the library just emits the
flight resource shape and Google infers transit context from the
resource type itself.
%WalletPasses.PassData{
serial_number: "BP-001",
pass_type: :boarding_pass,
transit_type: :train,
# ...
}Class-Shape Differences
Google's class resource has a "title" field with a different name per
type. The library reads class_config.event_name and writes it into
the right field for the pass type you're creating:
pass_type | Class title field | Localized sibling |
|---|---|---|
:event_ticket | eventName | (already LocalizedString) |
:boarding_pass | (no class title field — flight details carry the identity) | — |
:store_card | programName | localizedProgramName |
:coupon | title | localizedTitle |
:generic | (no class title field) | — |
Two patterns to internalise:
:event_ticketcarries its title as a nativeLocalizedStringfield (eventName). Translations populatetranslatedValuesdirectly.:store_cardand:couponcarry the title as a plain string with a separate localized sibling. The library always writes the plain field and adds the sibling (localizedProgramName,localizedTitle) only when at least one translation matches. See the Localization guide's "plain + localized sibling" section for the full pattern.
The same event_name value drives the class title regardless of type —
the library picks the destination field for you:
# Drives "eventName" on an event-ticket class.
WalletPasses.google_save_url(pass_data, google_visual,
class_config: %{
id: "summer_2026",
issuer_name: "Festival Co",
event_name: "Summer Music Festival",
pass_type: :event_ticket
}
)
# Drives "programName" on a loyalty class.
WalletPasses.google_save_url(pass_data, google_visual,
class_config: %{
id: "rewards_v1",
issuer_name: "Coffee Co",
event_name: "Rewards Program",
pass_type: :store_card
}
)
# Drives "title" on an offer class.
WalletPasses.google_save_url(pass_data, google_visual,
class_config: %{
id: "summer_promo",
issuer_name: "Summer Co",
event_name: "20% Off Everything",
pass_type: :coupon
}
):boarding_pass and :generic class objects don't take a single
"title" field — the library leaves event_name out of the class JSON
for those types. Configure identity through type-specific fields on
your own (flight details for boarding passes; field arrays for
generic).
API Reference
WalletPasses.PassType is a thin lookup module. Every function takes a
single pass-type atom and returns a string.
| Function | Returns (for :event_ticket) | Used by |
|---|---|---|
apple_style_key/1 | "eventTicket" | Apple pass.json top-level structure key |
google_object_type/1 | "eventTicketObject" | Google object resource path |
google_class_type/1 | "eventTicketClass" | Google class resource path |
google_save_objects_key/1 | "eventTicketObjects" | Save-URL JWT payload key |
google_class_suffix/1 | "event_class" | Default class ID suffix when none provided |
all/0 | [:event_ticket, :boarding_pass, :store_card, :coupon, :generic] | Validation / enumeration |
iex> WalletPasses.PassType.apple_style_key(:boarding_pass)
"boardingPass"
iex> WalletPasses.PassType.google_class_type(:store_card)
"loyaltyClass"
iex> WalletPasses.PassType.google_class_suffix(:coupon)
"offer_class"
iex> WalletPasses.PassType.all()
[:event_ticket, :boarding_pass, :store_card, :coupon, :generic]You generally don't need to call these directly — they're invoked by the
builders. They're public so you can switch on them in your own
PassDataProvider or wire them into custom telemetry without
hard-coding string literals.