Google Wallet
Drive geofenced notifications and custom messages on your users’ Google Wallet passes. Zeotap writes the merchantLocations geofence field on individual pass objects, clears it when a campaign ends, and can send custom pass messages — all keyed to the user’s own pass. The Android OS handles location detection; Zeotap only makes the API call at the right time.
Zeotap never creates passes or manages enrollment. It only modifies the merchantLocations field (and, optionally, adds a message) on passes your users have already saved to Google Wallet.
Prerequisites
- A Google Wallet issuer account (Google Pay & Wallet Console) with the Google Wallet API enabled.
- A GCP service account invited as a Developer on your issuer (see Authentication).
- Pass objects already issued to users, and each user’s Wallet object ID flowing into Zeotap as a profile attribute.
Authentication
Google Wallet uses Service Account (JWT) authentication. Zeotap signs a short-lived JSON Web Token with your service account key and exchanges it for an OAuth 2.0 access token scoped to wallet_object.issuer.
- In the Google Cloud Console, enable the Google Wallet API for your project.
- Create a service account (IAM & Admin → Service Accounts). It needs no GCP IAM roles — the Wallet API uses its own permission model.
- Create a JSON key for the service account and download it.
- In the Google Pay & Wallet Console (Users & Permissions), invite the service account’s email and grant it the Developer role on your issuer account.
- Paste the JSON key into the Service Account JSON field in Zeotap.
Configuration
| Field | Type | Required | Description |
|---|---|---|---|
| Service Account JSON | File | Yes | The GCP service account key JSON. Stored encrypted. |
| Issuer ID | Text | Yes | Your numeric Google Wallet issuer account ID (also the prefix of every object ID). |
| Default pass type | Select | No | The pass object collection to target when a sync or row does not supply one: loyaltyObject, offerObject, genericObject, giftCardObject, or eventTicketObject. Defaults to loyaltyObject. |
| Requests per second | Number | No | Client-side throttle ceiling. Defaults to Google’s quota of 20 requests/second. |
Target Settings
These are sync-level settings (they render in the sync’s Target Settings, not the field mapping):
| Field | Type | Required | Description |
|---|---|---|---|
| Action | Select | Yes | What to do to the pass: Activate geofences, Clear geofences, or Send message. In audience mirror mode, activate also clears automatically when a member exits the audience. |
| Message type | Select | No | TEXT_AND_NOTIFY pushes a device notification; TEXT adds the message silently (Send message only). |
The message content (header, body) is per-record data and lives in field mapping, not here — map a column to personalize per user, or set a fixed value. This keeps every field in exactly one place.
Supported Operations
Sync Modes: Update
Audience Sync Modes: Add, Remove, Mirror, Upsert
Audience mirror mode is the natural fit for the geofence lifecycle: a user entering the audience activates their geofences, and exiting clears them — no orchestration required. For event-driven campaigns (activate, wait, then clear, with an optional message), use a journey with Send to Destination steps.
Features
- Field Mapping: Yes
- Schema Introspection: No
Required Mapping Fields
Required fields are action-aware — the mapping enforces exactly what the selected action needs:
| Field | Required for | Description |
|---|---|---|
| wallet_object_id | all actions | The pass object ID to modify (e.g. 3388000000012345678.user-abc). Used as the API path — never sent in the request body. |
| merchant_locations | activate | The geofence coordinates: an array of { latitude, longitude }. Only the first 10 are used. |
| header | send_message | Notification title. A notification needs both a title and body. |
| body | send_message | Notification body. |
Default Destination Fields
| Field | Type | Applies to | Description |
|---|---|---|---|
| pass_type | string | all actions | Per-user override of the default pass type. Optional — leave unmapped to use the connection default; a row with no value and no default fails. |
| message_id | string | send_message | Optional Message.id. Use a stable value for idempotency so a re-running journey step does not push a duplicate notification. |
| display_start_time | string | send_message | Optional ISO-8601 timestamp — the message becomes visible on the pass from this time. |
| display_end_time | string | send_message | Optional ISO-8601 timestamp — the message stops showing after this time. |
How It Works
All three actions target the same object, addressed by wallet_object_id in the URL path:
- Activate —
PATCH /{passType}/{objectId}with the mappedmerchant_locations(capped at 10). The OS then surfaces the pass on the lock screen when the device dwells near one of those points. - Clear —
PATCH /{passType}/{objectId}with an emptymerchantLocationsarray, switching the geofence off. Only the geofence is removed; the pass itself is untouched. - Send message —
POST /{passType}/{objectId}/addMessagewith a custom header and body. This is an immediate push, not location-triggered.
Every call is a single per-object request (the Wallet API has no bulk endpoint). A PATCH replaces the whole merchantLocations array — there is no append — so Zeotap always sends the complete desired set.
Rate Limits
Google enforces roughly 20 requests per second per issuer. Zeotap paces outbound calls to the configured Requests per second ceiling and retries on 429 responses with exponential backoff. A large audience activation is therefore throughput-bound: 500,000 users at 20 req/s takes roughly 7 hours, so prefer journey-triggered activations for timely campaigns and reserve always-on audiences for smaller segments.
Send message caps. addMessage is separately limited to roughly 3 messages per object per 24 hours, and each TEXT_AND_NOTIFY message pushes a real device notification. Excessive or repetitive messaging can put the issuer account under review by Google, so treat send_message conservatively: map a stable message_id for idempotency (so re-runs don’t re-push), prefer TEXT over TEXT_AND_NOTIFY while testing to avoid live pushes, and test against a small set of dedicated test objects rather than the production issuer at volume.
Best Practices
- Order coordinates by relevance. Only the first 10
merchant_locationsare sent, so put the most relevant stores first. - Always clear. For offers, clearing after redemption is required; for other campaigns it keeps stale geofences from firing indefinitely. Audience mirror mode clears automatically on exit.
- Keep object IDs fresh. A user who removes the pass yields a
404; those rows are skipped and logged, not fatal.
Troubleshooting
403 Permission denied on every call
The service account has not been invited as a Developer on your issuer, or the Google Wallet API is not enabled for the project. Fix the invitation in the Google Pay & Wallet Console and re-run the connection test.
404 Pass object not found
The wallet_object_id does not exist — commonly because the user removed the pass from their wallet, or the ID convention feeding Zeotap is wrong. These rows are skipped and reported per-row; the batch continues.
400 Bad request on activate
Usually more than 10 locations, or malformed coordinates. Zeotap truncates to 10 automatically, so a 400 here typically means a coordinate value was non-numeric.
Geofence notification never appears on the device
Geofencing is Android-only and requires the user to have granted precise location access to Google Wallet. Detection is also not instant — Android batches location checks to save battery, so there can be a delay of several minutes after entering the ~150m radius.
The notification text can’t be customized
The geofence notification is system-generated (“You have a pass nearby”) and cannot be changed. For custom copy, use the Send message action, which delivers your own header and body (but is not location-triggered).
A user near many stores misses some notifications
Google caps geofenced notifications at 4 per user per day. A user passing more than four geofenced stores will only be notified at four of them.