# Cancel Batch Source: https://docs.passentry.com/api-reference/v1/batches/cancel-batch patch /api/v1/batches/{id}/cancel Cancel a Batch. Can only be used on scheduled batches. # Create Passes Batch Source: https://docs.passentry.com/api-reference/v1/batches/create-passes-batch post /api/v1/passes/batch Create passes in bulk via a CSV file. For more details, see the [Batch Pass Creation](/documentation/bulk-operations/batch-pass-creation/guide) documentation. # Create Wallet Messaging Batch Source: https://docs.passentry.com/api-reference/v1/batches/create-wallet-messaging-batch post /api/v1/entities/{id}/messaging/wallet Create a new wallet messaging batch using segments, recipients, or a CSV file (via multipart/form-data). The path parameter `{id}` is the entity **sending** the batch (your own entity). Recipients are defined separately in the request body via `segments`, `recipients`, or `csv`. Pass template and group tag segments can be combined to narrow the audience to passes that match both. For more details, see the [Wallet Messaging](/documentation/bulk-operations/batch-notifications/guide) documentation. # Batches Source: https://docs.passentry.com/api-reference/v1/batches/guide ## Overview In most integrations, you will often be interacting with a large number of passes or users at once. Although this can easily be done by making individual API calls, this can be inefficient and slow, especially if you are making a large number of requests. To address this, we support **Batches** for bulk operations. > [!NOTE] > > Although batch operations can be an efficient way to increase max request throughput, since they are processed asynchronously by PassEntry and not affected by your organisation's rate limits, at very low numbers of requests they can sometimes be slower than individual requests. ## Batch Types We support the following batch types: * **[Wallet Messaging Batch](/documentation/bulk-operations/batch-notifications/guide)** - Used to send push notifications to users, must be scheduled * **[Pass Creation Batch](/documentation/bulk-operations/batch-pass-creation/guide)** - Used to create passes in bulk **(Coming Soon to API, currently only available in Dashboard)** ## Batches API You can use the Batches API (`/api/v1/batches`) endpoints for general interactions with multiple batch types. These include: * [Show batch](/api-reference/v1/batches/show-batch) * [List batches](/api-reference/v1/batches/list-batches) * [Cancel batch](/api-reference/v1/batches/cancel-batch) (Used for scheduled batches) * [Send batch now](/api-reference/v1/batches/send-now-batch) (Wallet messaging batches only) # List Batches Source: https://docs.passentry.com/api-reference/v1/batches/list-batches get /api/v1/batches Retrieve a list of Batches # Send Batch Now Source: https://docs.passentry.com/api-reference/v1/batches/send-now-batch post /api/v1/batches/{id}/send-now Override a scheduled wallet messaging batch's `scheduledAt` time and start sending immediately. Only valid for batches currently in `scheduled` status. ## Best practices After creating a wallet messaging batch, it sits in `validating` while we verify the recipients and payload. Poll [`GET /api/v1/batches/{id}`](/api-reference/v1/batches/show-batch) until the status leaves `validating` before calling `send-now`. # Show Batch Source: https://docs.passentry.com/api-reference/v1/batches/show-batch get /api/v1/batches/{id} Retrieve a Batch by ID # Update Passes Batch Source: https://docs.passentry.com/api-reference/v1/batches/update-passes-batch post /api/v1/passes/batch/update Update passes in bulk via a CSV file. # Entities Source: https://docs.passentry.com/api-reference/v1/entities/guide **What are entities?** Entities serve as subdivisions within an organization, designed to help larger clients such as aggregators or resellers manage their operations more effectively. Each entity can represent a different department or business unit within the larger organization, enabling detailed segmentation and breakdowns. A common use case of having multiple entities is separating the passes in the Wallet (Most commonly an issue in Apple Wallet) as passes from different entities are not grouped in the Wallet. All organisations have one entity by default. For most organisations, this is sufficient. If you would like to create additional entities, please contact PassEntry. **Example** An organisation, "PassEntry Enterprise" has 2 business units, "PassEntry Supermarket" and "PassEntry Coffee". In order to keep their information and data separate, they have 2 *Entities*, each with their own *Pass Templates*, *Passes*...etc # List Entities Source: https://docs.passentry.com/api-reference/v1/entities/list-entities GET /api/v1/entities Lists all entities # Show Entity Source: https://docs.passentry.com/api-reference/v1/entities/show-entity get /api/v1/entities/{id} Retrieve an entity by ID # Create Image Asset Source: https://docs.passentry.com/api-reference/v1/image-assets/create-image-asset post /api/v1/image-assets Create a new image asset # Image Assets Source: https://docs.passentry.com/api-reference/v1/image-assets/guide Upload images once and reference them by ID across multiple passes Image Assets allow you to upload images once and reference them by ID when creating or updating passes. Instead of providing a raw image URL every time, you upload the image to PassEntry and use the returned `imageAssetId` in your `templateOverride`. Images can be provided as a **direct URL** or **base64 encoded data**. Supported formats: **PNG, JPG, WebP**. ### Supported image types | Type | Description | | - | - | | `banner` | Banner image displayed at the top of the pass | | `logo` | Logo image on the pass | | `thumbnail` | Thumbnail image (generic pass type only, Apple only) | ### Watermark If your organisation has watermarking enabled, a `watermark` object is required when uploading `banner` images. The watermark is applied automatically during processing. ```json theme={null} { "imageAsset": { "image": "https://example.com/silver_tier_banner.png", "imageType": "banner", "watermark": { "color": "light", "position": "bottomRight" } } } ``` | Field | Values | | - | - | | `color` | `dark`, `light` | | `position` | `bottomLeft`, `bottomRight`, `topLeft`, `topRight` | ### Workflow 1. **Upload an image** using [Create Image Asset](/api-reference/v1/image-assets/create-image-asset) with the image data and type 2. **Get the `id`** from the response 3. **Reference the `id`** in your pass `templateOverride` when creating or updating a pass ### Using Image Assets in templateOverride > [!NOTE] > > Template overrides must be enabled for your organisation. Contact support to request access. Requests made without this feature enabled will return a **403 Forbidden** error. When creating or updating a pass, reference an image asset by its ID in the `templateOverride` object: ```json theme={null} { "pass": { "templateOverride": { "bannerImage": { "imageAssetId": "19cb334f9696e64e10e7759f0" }, "logoImage": { "imageAssetId": "29dc445g0797f75f21f8860g1" } } } } ``` ### Using Image Assets in Batch Operations You can reference image assets in CSV batch operations using the following columns: | CSV Column | Description | | - | - | | `override_banner_image_asset_id` | UUID of a processed banner image asset | | `override_logo_image_asset_id` | UUID of a processed logo image asset | | `override_thumbnail_image_asset_id` | UUID of a processed thumbnail image asset (generic passes only) | For batch updates, set a column to the literal string `null` to remove an image asset override. See the [Batch Pass Creation](/documentation/bulk-operations/batch-pass-creation/guide) and [Batch Pass Update](/documentation/bulk-operations/batch-pass-update/guide) guides for full details. ### Deprecated format The previous flat URL format for image overrides is deprecated but still supported. Image URLs must use **HTTPS** or be a **base64-encoded data URI** (`data:image/...`). Plain HTTP URLs are rejected. ```json theme={null} { "pass": { "templateOverride": { "bannerImage": "https://example.com/banner.png" } } } ``` We recommend migrating to the `imageAssetId` object format for better image management and reduced duplication. # List Image Assets Source: https://docs.passentry.com/api-reference/v1/image-assets/list-image-assets get /api/v1/image-assets List all image assets # Show Image Asset Source: https://docs.passentry.com/api-reference/v1/image-assets/show-image-asset get /api/v1/image-assets/{id} Show an image asset by ID # Authentication Source: https://docs.passentry.com/api-reference/v1/introduction/authentication The PassEntry API uses API keys to authenticate requests. You can view and manage your API keys in [Developer tools](https://app.passentry.com/developer-tools) in the PassEntry Dashboard Your API keys carry many privileges, so be sure to keep them secure. Don't share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth. Authentication to the API is performed with Bearer authentication. These tokens must be attached to the Authorization header of the request. Example: ``` "Authorization": "Bearer {API KEY}" ``` You must make all API calls over [HTTPS](http://en.wikipedia.org/wiki/HTTP_Secure). Calls that you make over plain HTTP will fail. API requests without authentication will also fail. # Deprecation Source: https://docs.passentry.com/api-reference/v1/introduction/deprecation PassEntry commits to maintaining backwards compatibility while evolving our API. > [!NOTE] > > Deprecated endpoints and parameters remain functional after deprecation, with continued bug fixes and no breaking changes. ## Notifications We notify users of deprecations through: * API documentation updates * Email notifications (via newsletter or other communication channels) * Changelog entries ## Identification Deprecated features are marked with a "Deprecated" label in documentation with alternative suggestions. ## Migration Whenever possible, please migrate to the non-deprecated endpoints and parameters. Deprecated endpoints and parameters will be removed in a future version of the API. We recommend migrating to the replacement features as soon as possible. For migration assistance, contact our support team at [support@passentry.com](mailto:support@passentry.com). # Errors Source: https://docs.passentry.com/api-reference/v1/introduction/errors PassEntry uses conventional HTTP response codes to indicate the success or failure of an API request. In general: Codes in the `2xx` range indicate success. Codes in the `4xx` range indicate an error that failed given the information provided (e.g., a required parameter was omitted, syntax error, etc.). Codes in the `5xx` range indicate an error with PassEntry's servers (these are rare). Some `4xx` errors include text that briefly explains the error reported. # Rate Limiting Source: https://docs.passentry.com/api-reference/v1/introduction/rate-limiting The PassEntry API implements rate limiting to ensure service stability and fair usage across all users. Rate limits are applied separately to each combination of endpoint and HTTP method. For example, you can make up to 10 GET requests per second to `/passes` and simultaneously 10 POST requests per second to the same endpoint. ## Rate Limit Implementation We use a sliding window algorithm to track and enforce rate limits. While the rate limit is 10 requests per second, we recommend implementing a buffer in your applications to account for network latency and potential timing variations. > [!TIP] > > Best practice is to limit your requests to 9 requests per second, leaving some buffer for retries and network delays. This helps prevent edge cases where requests might be counted in different time windows. ## Response Headers The API includes rate limit information in the response headers: | Header | Description | | - | - | | `x-rate-limit-limit` | Maximum number of requests allowed per second (10) | | `x-rate-limit-remaining` | Number of requests remaining in the current window | | `x-rate-limit-reset` | Unix timestamp when the rate limit window resets | | `x-rate-limit-url` | The endpoint path this rate limit applies to | ## Rate Limit Exceeded When you exceed the rate limit, the API will respond with a `429 Too Many Requests` status code and include an additional header: | Header | Description | | - | - | | `x-rate-limit-retry-after` | Time in seconds (decimal) to wait before retrying | ### Example Rate Limited Response ```json theme={null} { "errors": [ "Too Many Requests" ] } ``` ## Handling Rate Limits ### Best Practices * Implement exponential backoff with jitter for retries * Cache responses when possible to reduce API calls * Use bulk operations where available instead of multiple single requests * Implement retries if the request fails due to rate limiting # Responses Source: https://docs.passentry.com/api-reference/v1/introduction/responses PassEntry API responses are generally in [JSON API](https://jsonapi.org/format/) format, which provides a consistent standard for formatting responses in JSON, including a top-level `data` object and optional `included` and `meta` objects. # Welcome to PassEntry API Source: https://docs.passentry.com/api-reference/v1/introduction/welcome PassEntry API is an OpenAPI 3.1.0 compliant REST API with predictable URLs, JSON request bodies, standard HTTP response codes, authentication, and verbs. This documentation describes the functionality of our system available for integration with your business. **Base URL** ```Base URL theme={null} https://api.passentry.com ``` # Loyalty Source: https://docs.passentry.com/api-reference/v1/loyalty/guide PassEntry provides a number of RESTful endpoints for interacting and manipulating passes created under the Loyalty vertical.\ This includes but is not limited to: Loyalty Cards, Gift Cards, Coupons, Memberships… ### Terminology **Balance**: This can represent loyalty points, stamps, credit, and more in an integer type. Balance can be accumulated or used. Balance is stored in our database and represented on the pass. **POS System:** handles in store sales transactions, inventory management, and reporting **Loyalty Program:** This is implemented externally to PassEntry, is where the loyalty behaviour is handled and loyalty/customer data is stored. Ideally, this would be closely integrated with the POS system, in order to allow for seamless tracking of purchases and points. *As a best practice, any loyalty data relevant to a pass must always be mirrored in the loyalty program*. **Payment Terminal:** where payment is made, can also be used to read the loyalty pass and thus identify the customer ### Enabling loyalty balance To allow interacting with your passes using the Loyalty API, the first step is to ensure that the parent template has loyalty balance enabled. Once this is done, you must choose how you want the balance to be represented on the pass, set at the pass template level. We currently offer support for **integer** and **decimal** types. * **Integer:** Choose this if your balance is represented as whole numbers. *Eg: 3, 42, 9000*. The loyalty balance will appear as an integer in our responses to the loyalty API, and any numeric value sent in a request will be coerced to an integer. The value on the pass will be represented as an integer. * **Decimal:** We support up to 2 decimal places. *Eg: 3.14, 10.95, 100000.34*. The loyalty balance will appear as a float in our responses to the loyalty API. When making requests, format the number as a float with no more than 2 decimal places. When displaying the value on the pass, it will be represented using thousand separators with 2 decimal places. (100,000.34) *** ### What does a loyalty integration with PassEntry look like? **Example**: PassEntry Coffee has a loyalty program. Every £10 spent, a customer receives 1 `Point`\ When a customer has received 5 points they are able to get £5 off on their order total **Distributing a loyalty pass** 1. A loyalty pass is created and distributed to a customer with an `ext_id` equivalent to customer in the loyalty program 2. The loyalty pass balance on this loyalty pass is `0`. This is represented on the loyalty pass as `Points 0` and is stored on the loyalty program. 3. When the customer makes a purchase in store and presents their device to make payment, the reader identifies their loyalty pass and sends both the loyalty pass `ext_id` and the loyalty pass balance to the POS **Accumulating balance** 4. The POS system takes payment, and updates the loyalty pass with a balance equal to a value determined by the loyalty program, alongside an optional push notification. 5. In this example, the customer has spent £20, so the balance is increased by 2 points and the customer receives `Thank you for your order. You've earned 2 points!` as a push notification. **Receiving a reward** 5. The POS system takes £10 payment and customer balance is increased by 1 point. 6. As the threshold of 5 points has been reached, as determined by the loyalty program, the customer is sent a push notification `Congratulations! You've got £5 off on your next order!` 7. The pass design can be updated via the `/passes/` endpoint so that the `£5 off` reward appears on the loyalty pass design to inform the customer of their reward **Redeeming a reward** 8. The POS system is ready to receive payment of £8 9. The customer presents their device, and their loyalty pass `ext_id` and balance are sent to the POS system, identifying the customer to the loyalty program 10. The loyalty program detects that the customer has 5 points and is thus entitled to a £5 off discount 11. The POS system prompts the user to redeem their `£5 off!` reward via a `Would you like get £5 off on this order?` prompt (**Optional:** rewards can be applied automatically as determined by the loyalty program) 12. The transaction amount on the payment terminal is updated to £3 13. The customer makes payment 14. The reward is removed from the loyalty pass alongside a `You've used your £5 off discount!` push notification **Resetting the balance (Optional)** 17. At this stage, the loyalty balance can be reset to 0 points # Show loyalty balance Source: https://docs.passentry.com/api-reference/v1/loyalty/show-loyalty-balance get /api/v1/loyalty/{id} # Update loyalty balance Source: https://docs.passentry.com/api-reference/v1/loyalty/update-loyalty-balance patch /api/v1/loyalty/{id} # Create Pass Template Source: https://docs.passentry.com/api-reference/v1/pass-templates/create-pass-template post /api/v1/pass-templates Pass Templates are created asynchronously and the response will return a 202 status code. Use the URL returned in the response to check the status of the pass template. # Show Pass Template (Deprecated) Source: https://docs.passentry.com/api-reference/v1/pass-templates/deprecated-show-pass-template get /api/v1/pass_templates/{pass_template_uuid} Retrieve a Pass Template by ID (This endpoint is deprecated) # Pass templates guide: create, update, and manage designs Source: https://docs.passentry.com/api-reference/v1/pass-templates/guide Learn how to create, update, and manage PassEntry pass templates from the Dashboard or API, including fields, colours, images, and value keys. Before you can create passes using the PassEntry API, you need a pass template. A template serves as a blueprint for your passes and includes various components such as fields, colours, images and value keys. ## Create a pass template You can create a pass template directly from the Dashboard, on the Pass Templates page. Click **Create Pass Template** in the top right corner (direct link: [https://app.passentry.com/pass-templates/create](https://app.passentry.com/pass-templates/create)). Once created, the pass template appears in the Pass Templates page. ### See the pass templates list You can view all pass templates on the Pass Templates page of your Dashboard (direct link: [https://app.passentry.com/pass-templates](https://app.passentry.com/pass-templates)). ## Update a pass template You can update pass templates via the Dashboard or via the [Update Pass Template API](/api-reference/v1/pass-templates/update-pass-template). PassEntry applies template-level changes such as colours, images and field labels to all existing passes when the template updates. ### Field default values do not propagate by default When you change a field's `defaultValue` on a template, the new value is **only applied to newly created passes**. Existing passes keep whatever value they already have on that field. To push the new default value out to all existing passes that use the template, set `overrideFieldValue: true` on the field in your update request. Fields are keyed by section (`header`, `central`, `details`, `back`) and then by position (`one` to `six`): ```json theme={null} { "passTemplate": { "fields": { "header": { "one": { "id": "tier", "defaultValue": "Gold", "overrideFieldValue": true } } } } } ``` `overrideFieldValue` is accepted on [Update Pass Template](/api-reference/v1/pass-templates/update-pass-template) only. Sending it on [Create Pass Template](/api-reference/v1/pass-templates/create-pass-template) returns a validation error. #### When the override applies An existing pass is updated only when all of the following are true: * You send a new `defaultValue` for the field in the same request. `overrideFieldValue: true` on its own does nothing. * The new `defaultValue` differs from the one currently stored on the template. * The pass already carries that field `id`. Passes that do not have the field yet pick up the new default regardless of `overrideFieldValue`. PassEntry updates only live passes and leaves nullified and expired passes untouched. > [!WARNING] > > `overrideFieldValue: true` replaces the value on every live pass, including values you set per pass through the Update Pass API or a batch pass update. Those per-pass values are lost. Use `overrideFieldValue` only when updating the default value of a field. To change individual pass values without touching the template default, use the [Update Pass API](/api-reference/v1/passes/update-pass) or the [Batch Pass Update guide](/documentation/bulk-operations/batch-pass-update/guide) instead. The same flag controls localised default values. See [Localisation](/documentation/passes/localisation/guide) for details. ### NFC and barcode changes do not propagate by default Changes to the `nfc` or `barcode` objects on a template follow the same rule as field default values. By default, the new configuration **only applies to newly created passes**. Existing passes keep their current NFC or barcode settings. To push a new NFC configuration out to all existing passes, set `overrideNfc: true` inside the `nfc` object on your update request: ```json theme={null} { "passTemplate": { "nfc": { "enabled": true, "source": "uuid", "overrideNfc": true } } } ``` To push a new barcode configuration out to all existing passes, set `overrideBarcode: true` inside the `barcode` object: ```json theme={null} { "passTemplate": { "barcode": { "enabled": true, "type": "qr", "source": "uuid", "displayText": false, "overrideBarcode": true } } } ``` [Update Pass Template](/api-reference/v1/pass-templates/update-pass-template) accepts both flags. [Create Pass Template](/api-reference/v1/pass-templates/create-pass-template) ignores these flags since there are no existing passes to override. #### Per-pass resolution When an override runs, PassEntry resolves the value for each existing pass: * `source: "uuid"` uses each pass's UUID as the NFC message or barcode value. * `source: "extId"` uses each pass's `extId`. Passes without an `extId` are skipped and keep their existing settings. * `source: "custom"` uses the `customValue` (for NFC) or `value` (for barcode) from your update request. For NFC, passes belonging to an entity that does not have NFC access are also skipped. > [!WARNING] > > `overrideNfc: true` and `overrideBarcode: true` replace the NFC or barcode configuration on every live pass on the template. This includes values you set per pass through the Update Pass API. Those per-pass values are lost. Use these flags only when you want the new template-level NFC or barcode configuration to apply to existing passes. To change NFC or barcode values on individual passes without touching the template, use the [Update Pass API](/api-reference/v1/passes/update-pass) instead. # List Pass Templates Source: https://docs.passentry.com/api-reference/v1/pass-templates/list-pass-templates get /api/v1/pass-templates List Pass Templates # Show Pass Template Source: https://docs.passentry.com/api-reference/v1/pass-templates/show-pass-template get /api/v1/pass-templates/{id} Show a Pass Template # Update Pass Template Source: https://docs.passentry.com/api-reference/v1/pass-templates/update-pass-template patch /api/v1/pass-templates/{id} Supports partial updates.
Response may either be a 200 or 202 status code. Use the URL returned in the response to check the status of the pass template. # Create pass Source: https://docs.passentry.com/api-reference/v1/passes/create-pass post /api/v1/passes # List passes Source: https://docs.passentry.com/api-reference/v1/passes/list-passes get /api/v1/passes # Nullify Pass Source: https://docs.passentry.com/api-reference/v1/passes/nullify-pass post /api/v1/passes/{id}/nullify Irreversibly nullifies a pass, removing all dynamic data (fields, images, barcode, NFC, localisation) while preserving the tombstone record for billing and auditability. The pass will appear in the expired section of the wallet with a generic "Deactivated" design. The `extId` is preserved on the nullified record and can be reused when creating new passes. This endpoint is idempotent — calling it on an already-nullified pass returns 200 with no further changes. # Send Notification Source: https://docs.passentry.com/api-reference/v1/passes/send-notification post /api/v1/passes/{id}/notification # Show Pass Source: https://docs.passentry.com/api-reference/v1/passes/show-pass get /api/v1/passes/{id} # Update pass Source: https://docs.passentry.com/api-reference/v1/passes/update-pass patch /api/v1/passes/{id} # Create reader Source: https://docs.passentry.com/api-reference/v1/readers/create-reader post /api/v1/entities/{entity_id}/readers # Get reader Source: https://docs.passentry.com/api-reference/v1/readers/get-reader get /api/v1/readers/{id} # Get reader configuration Source: https://docs.passentry.com/api-reference/v1/readers/get-reader-configuration get /api/v1/entities/{entity_id}/readerConfiguration # Readers Source: https://docs.passentry.com/api-reference/v1/readers/guide Readers can be used for a variety of use cases, to validate access, redeem loyalty, claim discounts, identify the user and much more. **NFC** PassEntry's passes are NFC enabled, which allow you to set an NFC 'value' behind every pass. This value defaults to the UUID of the pass, or can be set to a custom value. This NFC value is encrypted, and can only be generated by PassEntry. In order for the NFC value to be read, an NFC reader must be configured. To do this, two things are required, a pass identifier, which tells the reader which passes to request the device for when "tapped", and a private key, which allows the reader to decrypt the NFC value. Our system supports two types of Readers: **VTAP Readers** and **PassEntry Reader App**. Both types can be configured through the PassEntry Dashboard or via API requests. Below, we provide an overview of each reader type, the configuration process, and additional setup information. ## VTAP Readers **VTAP Readers** come in a range of form factors, from boxed, countertop devices to embedded hardware that can be integrated in existing platforms. The NFC value can be transmitted over the internet, or over wire through USB, RS-232, RS-485, Wiegand and more. #### Configuration: Setting up a VTAP Reader consists of two parts: 1. **Creating a Reader on PassEntry**: You need to provide the following information: * **Name**: A friendly display name for the reader * **UUID**: A unique identifier for the reader, this must be the "Reader Serial" provided by Dot Origin * **Type**: Should be set to `VtapReader` 2. **Setting up the Device on VTAP Website**: After creating the reader on PassEntry, retrieve the required configuration information and use it to set up the device on the VTAP website. More detailed instructions are provided in the guide. ## PassEntry Reader App **PassEntry Reader** is a software application available for download from the Google Play Store, MAXSTORE, and Sunmi App Store. This reader offers additional functionality and settings for scanning NFC passes, QR codes/barcodes, Mifare Classic NFC cards, and more. It can perform specific actions such as scanning taps, redeeming/adding points, and adding stamps. For more information, please [contact PassEntry](mailto:support@passentry.com). # Reader heartbeat Source: https://docs.passentry.com/api-reference/v1/readers/reader-heartbeat get /api/v1/readers/{id}/heartbeat Keep a reader session alive and mark the reader as online in the Dashboard. Call it every 5 minutes with the current session token. Each successful heartbeat returns a new token and refresh token and invalidates the previous ones, so switch to the returned token immediately. # Reader login Source: https://docs.passentry.com/api-reference/v1/readers/reader-login post /api/v1/readers/login Sign in as a PassEntry Reader using its Reader ID and password, exactly as the PassEntry Reader app does. Returns a session token, a refresh token and the reader's NFC configuration. A reader holds one session at a time: logging in again invalidates the previous session's tokens. Keep the session alive with Reader heartbeat every 5 minutes. # Reader logout Source: https://docs.passentry.com/api-reference/v1/readers/reader-logout delete /api/v1/readers/{id}/logout End the reader session. The session token and refresh token are invalidated and the reader is marked as logged out in the Dashboard. # Refresh reader token Source: https://docs.passentry.com/api-reference/v1/readers/refresh-reader-token post /api/v1/readers/refresh_token Exchange a refresh token for a new session token and refresh token without the reader password. Use it when an API call returns 401 because the session token expired. The previous tokens are invalidated. # Update reader Source: https://docs.passentry.com/api-reference/v1/readers/update-reader patch /api/v1/readers/{id} # Create scan Source: https://docs.passentry.com/api-reference/v1/scans/create-scan post /api/v1/scans # Get scan Source: https://docs.passentry.com/api-reference/v1/scans/get-scan get /api/v1/scans/{id} # List scans Source: https://docs.passentry.com/api-reference/v1/scans/get-scans get /api/v1/scans # Scans Source: https://docs.passentry.com/api-reference/v1/scans/guide Overview of the Scans API and the two validation methods available. ## **Overview** A scan represents the action of reading a pass (either via NFC or barcode) using a **PassEntry-compatible reader**. The **Scans API** is essential for businesses that need to validate and process passes at physical locations. **Common Use Cases** * Event access control * Membership verification * Ticket validation * Loyalty program check-ins * etc. ## **Validation Methods** The **Scans API** enables validation of passes through two different methods: ### **Internal PassEntry Validation (Default)** * A simple and convenient out-of-the-box solution * Validates passes against **PassEntry's database** * Ensures the pass is **valid and active** * If a validity period is configured, ensures the pass **hasn't been scanned within that timeframe** * Returns **immediate validation results** and **reader instructions** * Scan value (NFC/barcode) must be the **pass UUID** or **external ID** * `/scans` endpoint is used for this flow *** ### **Third-Party Validation** * Your system handles **all validation logic** * Highly flexible – define your own validation rules and business logic * Scan value can contain **any data format** you need: * Multiple values (e.g., `"customerID-promoCode"`) * Custom formats * Full control over **reader behavior** through reader instructions: * Currently supports **display messages** * Future expansion planned for **LED colors, sounds, icons**, and more controls depending on the reader type * Results are **logged back to PassEntry** for analytics and reporting (only available for PassEntry Reader) * `/scans/log` endpoint is used for this flow * Available with **PassEntry Reader** and **VTAP reader** *** Both methods support **NFC and barcode scanning**, and provide **detailed scan information**, including timestamps, pass details, and validation status. The **validation type is configured in your [reader settings](https://app.passentry.com/readers/reader-configuration)** on the **PassEntry Dashboard**. # Log scan Source: https://docs.passentry.com/api-reference/v1/scans/log-scan post /api/v1/scans/log # Create Webhook Subscription Source: https://docs.passentry.com/api-reference/v1/webhook-subscriptions/create-webhook-subscription post /api/v1/webhook-subscriptions Create a new webhook subscription to notify your specified target URL about events happening within PassEntry. # Delete Webhook Subscription Source: https://docs.passentry.com/api-reference/v1/webhook-subscriptions/delete-webhook-subscription delete /api/v1/webhook-subscriptions/{id} Delete an existing webhook subscription. # Webhook Subscriptions Source: https://docs.passentry.com/api-reference/v1/webhook-subscriptions/guide ## Webhook Subscriptions Webhook subscriptions allow you to receive real-time notifications about events in your PassEntry account. When an event occurs (like a pass being installed or scanned), PassEntry will send an HTTP POST request to your specified endpoint with details about the event. > [!WARNING] > > This section covers webhook subscriptions and management. For webhook events and security best practices, see the [Webhooks](/api-reference/v1/webhooks/guide) section. ### Setting Up Webhooks To start receiving webhooks, create a subscription either via API or [PassEntry Dashboard](https://app.passentry.com/developer-tools). 1. Create a webhook subscription by providing: * An HTTPS endpoint URL that will receive the webhook events * A list of events you want to subscribe to * Optional entity scope if you want to receive events only for a specific entity 2. Store the secret key returned in the response securely. This key is used to verify webhook signatures (see [Security Measures](/api-reference/v1/webhooks/guide#security-measures)). 3. Configure your endpoint to: * Accept POST requests * Verify webhook signatures * Return a 2xx status code to acknowledge receipt ### Webhook Management #### Scoping * Your entire organisation (default) * A specific entity (if you have multiple entities) **Important Limitations:** * Cannot create multiple webhook subscriptions with the same target URL within the same scope * Cannot change the scope after subscription creation (create a new one and delete the old one) * Default limits: 10 organisation-level and 10 per entity subscriptions #### Status * **Enabled**: Actively receiving events * **Disabled**: Temporarily paused ### Debugging and Security * Use the "Test webhook" functionality via [API](/api-reference/v1/webhook-subscriptions/test-webhook-subscription) or [Dashboard](https://app.passentry.com/developer-tools) to debug your subscriptions * If you believe your secret key was compromised, rotate it via [Dashboard](https://app.passentry.com/developer-tools) or [API](/api-reference/v1/webhook-subscriptions/rotate-secret-key) * Maintain separate webhook subscriptions for each environment (development, staging, production) to isolate testing and ensure proper event handling across your deployment pipeline For implementation details and best practices, see our [Webhooks Guide](/api-reference/v1/webhooks/guide). # List Webhook Subscriptions Source: https://docs.passentry.com/api-reference/v1/webhook-subscriptions/list-webhook-subscriptions get /api/v1/webhook-subscriptions Get a list of all webhook subscriptions with pagination and filtering options # Rotate Secret Key Source: https://docs.passentry.com/api-reference/v1/webhook-subscriptions/rotate-secret-key patch /api/v1/webhook-subscriptions/{id}/rotate-secret Generate a new secret key for webhook signature verification. The old key becomes invalid immediately. # Show Webhook Subscription Source: https://docs.passentry.com/api-reference/v1/webhook-subscriptions/show-webhook-subscription get /api/v1/webhook-subscriptions/{id} Get details of a specific webhook subscription based on ID. # Test Webhook Subscription Source: https://docs.passentry.com/api-reference/v1/webhook-subscriptions/test-webhook-subscription post /api/v1/webhook-subscriptions/{id}/test Send a test webhook to verify endpoint configuration and webhook handling. # Update Webhook Subscription Source: https://docs.passentry.com/api-reference/v1/webhook-subscriptions/update-webhook-subscription patch /api/v1/webhook-subscriptions/{id} Update an existing webhook subscription configuration. > [!WARNING] > > Note: The scope (organisation/entity) of a webhook subscription cannot be changed after creation. To change the scope, create a new webhook subscription and delete the old one. # Receiving webhooks from PassEntry Source: https://docs.passentry.com/api-reference/v1/webhooks/guide Set up an HTTPS endpoint to receive PassEntry webhook events, verify the X-Webhook-Signature HMAC-SHA256 header, and handle retries. # Incoming Webhooks with PassEntry ## Overview When building integrations with PassEntry, you might want your application to receive events as they occur, so that your backend systems can execute actions accordingly. To enable webhooks, you must first register a valid HTTPS URL, and subscribe to the events you want to be notified by. To do this, please visit the Webhook section on the PassEntry dashboard. ### Setting Up Your Webhook Endpoint * **HTTPS Requirement**: Ensure your endpoint is a valid HTTPS URL and the path can receive POST requests. ### Receiving Webhook Events * **Event Notification**: Upon the occurrence of a subscribed event, PassEntry will send a JSON payload to your endpoint, detailing the event type, timestamp, and relevant information. ### Request headers Each incoming webhook `POST` request from PassEntry includes the following headers: | Header | Value | | - | - | | `Content-Type` | `application/json` | | `X-Webhook-Signature` | HMAC-SHA256 hex digest of the raw request body, signed with your webhook secret key. See [Security Measures](#security-measures). | Header names only use characters from `[-A-Za-z0-9]`. PassEntry does not send additional custom headers on incoming webhooks. ## Security Measures We implement a number of security measures to guarantee any interactions between PassEntry and your system are verified and authentic. * **Secret Key**: When configuring your webhook endpoint, you will be provided a secret key, which PassEntry will use to sign any outbound webhooks. You must store this securely. If you believe your secret key has been compromised, you have the option to **rotate** your secret key. This will immediately invalidate the previous secret key and sign all subsequent webhooks with the new key * **Timestamp**: Each webhook payload includes a timestamp, serving as a safeguard against replay attacks. * **HMAC-SHA256 Signature**: Every outgoing webhook includes an HMAC signature. The signature is included in the `X-Webhook-Signature` header and is generated using the webhook payload and the shared secret key. To verify that the webhook is authentic, you can encrypt the webhook payload using the SHA-256 encryption algorithm and your secret key. If your generated signature matches the signature included in the `X-Webhook-Signature` header, you can confirm that the webhook is authentic. ## Reliability and Retries * **Asynchronous Delivery**: Webhooks include a timestamp to reflect the actual event time. * **Retry Mechanism**: Failures in delivery due to network issues, SSL problems, or non-2xx HTTP responses will trigger retries. PassEntry retries delivery up to 10 times over 3 days, using exponential backoff. ## Responding to Webhooks * **Success Responses**: Please ensure your endpoint returns a status code within the 200-299 range. Any code outside this range is considered an error, prompting retries. * **Response Body**: While optional, you may return a response body. ### Example Success Response ```http theme={null} HTTP Status: 200 Content-Type: application/json { "status": "ok" } ``` # Pass Created Source: https://docs.passentry.com/api-reference/v1/webhooks/pass-created webhook passCreated Webhook triggered when a pass is created # Pass Installed Source: https://docs.passentry.com/api-reference/v1/webhooks/pass-installed webhook passInstalled Webhook triggered when a pass is added to the wallet by user # Pass Removed Source: https://docs.passentry.com/api-reference/v1/webhooks/pass-removed webhook passRemoved Webhook triggered when a pass is removed from the wallet by user # Pass Scanned Source: https://docs.passentry.com/api-reference/v1/webhooks/pass-scanned webhook passScanned Webhook triggered when an NFC or barcode pass is scanned by a reader # Pass Tapped (Deprecated) Source: https://docs.passentry.com/api-reference/v1/webhooks/pass-tapped webhook passTapped Webhook triggered when an NFC pass is tapped on a reader (This webhook is deprecated, please use the passScanned webhook instead) # Product updates Source: https://docs.passentry.com/changelog/updates Follow release notes for the PassEntry Dashboard, API, and SDKs, including SSO improvements, new endpoints, and pass template features. **October 2026** ## Square Google banners Google banners can now be **Wide** (1032px x 336px) or **Square** (1032px x 812px). Transit templates support wide only. Learn more: [Pass design guide](/documentation/passes/design/guide) **September 2026** ## Delete pass templates from the Dashboard You can now delete pass templates directly from the Dashboard's templates list. Templates get deleted and all associated passes will be **nullified**. This is an irreversible action. Only published templates count against your template limit Open the "…" menu on any template card and pick **Delete**. Learn more: [Pass nullification](/documentation/passes/nullification/guide) **September 2026** ## Apple Generic Poster Design Apple's poster face is no longer limited to event tickets. Set `appleEnhancedDesign.type` to `generic` on an event ticket, generic or loyalty template to give its passes a full-bleed poster on iOS 27. Your fields sit over the artwork, and anything that does not fit moves into the information panel underneath. * Unlike the `eventTicket` design, the `generic` design needs no NFC or pass binding entitlement. Artwork is still required. * Each pass carries its standard layout alongside the poster, so devices below iOS 27 render the pass as they always have. * Add up to two `featuredActions` buttons under the poster. Each slot takes an action `type` and an `https://` URL, and the two slots must use different types. * Passes inherit the template's actions when they are created. Override a slot per-pass under `enhancedData.generic.featuredActions`, or send the slot as `null` to remove it. ![Apple Wallet poster pass showing artwork, fields over the chin and two featured action buttons](https://mintcdn.com/passentry-0/1mlKidz_QLv_-t2a/changelog/images/apple-poster-generic-pass.jpg?fit=max\&auto=format\&n=1mlKidz_QLv_-t2a\&q=85\&s=ee595ea1354474f35e7ed24a65b05a1c) Check out the guide here: [Apple Poster Designs](/documentation/passes/apple-poster-designs/guide) > [!WARNING] > > This feature is in **closed beta**.\ > Please contact PassEntry for access. **September 2026** ## Numbers in dynamic pass fields Dynamic pass fields defined on your template now accept **integers and floats** as values, in addition to strings and `null`. This applies both to the top-level `value` and to per-locale `localisation..value` entries on the [Create Pass](/api-reference/v1/passes/create-pass) and [Update Pass](/api-reference/v1/passes/update-pass) endpoints. Send a numeric quantity (points, tier number, price) without pre-formatting it as a string. Validation stays strict, so PassEntry still rejects unsupported types. ```json theme={null} { "pass": { "points": { "value": 1250, "localisation": { "fr": { "value": 1250 } } } } } ``` Learn more: [Pass value localisation](/documentation/passes/localisation/guide#pass-value-localisation) **August 2026** ## Combine pass template and group tag filters in wallet messaging Wallet messaging batches can now target passes by **pass template and group tags together**. Previously you could filter by one or the other; combining them narrows the audience to passes on the chosen template that also carry at least one of the selected tags. * In the Dashboard, tick both **By pass template** and **By group tag** when filtering recipients - the counter shows the combined audience before you send * Via the API, include a `passTemplateId` segment and a `groupTags` segment in the same `segments` array ```json theme={null} "segments": [ { "passTemplateId": "bf915c229114", "groupTags": ["summerPromotion", "easterOffer"] } ] ``` Learn more: [Create Wallet Messaging Batch](/api-reference/v1/batches/create-wallet-messaging-batch) **July 2026** ## Okta single sign-on for Dashboard Teams can now sign in to the PassEntry Dashboard with Okta. On the login page, choose **Sign in with Okta** and enter your work email — we'll route you to your organisation's Okta tenant automatically based on your email domain. * Email-based provider discovery — enter your work email and we redirect to the right Okta tenant * Per-organisation Okta configuration, scoped to an allowed set of email domains * Works alongside existing Google and Microsoft SSO options > [!NOTE] > > Okta SSO is available on request. Contact [support@passentry.com](mailto:support@passentry.com) to enable it for your organisation and configure your Okta tenant. **July 2026** ## Draft Pass Templates You can now save pass templates as **drafts** while you iterate on them, before making them live. Drafts let you experiment with different template designs — new layouts, fields or colours — without affecting your published catalogue or existing passes. * Create and edit drafts in the Dashboard while a template is still being designed * Publish a draft when it's ready to issue passes from; delete the ones you no longer need * Drafts are capped at 20 per organisation, but **don't count against your pass template quota** — feel free to keep a working set as you experiment * Every pass template response now includes a `status` field (`"draft"` or `"published"`) * `GET /api/v1/pass-templates` accepts an optional `?status=` filter (comma-separated list of `draft` or `published`) Learn more: [Draft and published templates](/documentation/pass-template-types-explained#draft-and-published-templates) **April 2026** ## Apple Poster Event Ticket Support We've added support for Apple's poster event ticket experience (iOS 18+), allowing you to create full-bleed event tickets with rich event information. **Pass Templates:** * Enable poster event tickets via the `appleEnhancedDesign` field * Configure artwork, secondary logo, and venue map images at the template level **Passes:** * Add rich event data including event details, venue information, seating, and event guide URLs via the `enhancedData` field * Override template images per-pass using the new `artworkImage`, `secondaryLogoImage`, and `venueMapImage` fields in `templateOverride` > [!WARNING] > > This feature requires NFC entitlement and is in **closed beta**.\ > Please contact PassEntry for access. **April 2026** ## Pass Nullification You can now permanently nullify a pass via the API, irreversibly removing all dynamic data (fields, barcode, NFC, images) while preserving a tombstone record for billing and compliance. Nullified passes appear as "Deactivated" in the wallet, and their External ID is freed for reuse. Check out the guide here: [Pass Nullification](/documentation/passes/nullification/guide), [Nullify Pass endpoint](/api-reference/v1/passes/nullify-pass) **March 2026** ## Pass Binding in open beta Pass binding restricts a digital pass to a specific wallet account, preventing sharing or unauthorized use. **Google Wallet** — The pass is restricted to a specific Google account using a SHA-256 email hash. Only the matching account can save the pass. **Apple Wallet** — The pass is bound to the user's device using FIDO hardware key authentication, requiring Face ID or Touch ID. Apple binding is mainly beneficial for NFC passes, as it disables the NFC on the pass. API docs here: [Create Pass](/api-reference/v1/passes/create-pass) Check out the guide here: [Pass Binding](/documentation/passes/pass-binding/guide) > [!WARNING] > > This feature is in **open beta** and can be requested via a custom plan or reach out directly to [support@passentry.com](mailto:support@passentry.com) - let us know if you encounter any issues or have feedback. **March 2026** ## Geofencing Improvements on Dashboard We've enhanced Geofencing in the new pass template builder with a unified map view and overlap warnings to prevent duplicate notifications. We've also clarified Apple vs Google behaviour, including device-specific settings and limitations. **March 2026** ## Image Assets API Upload images once and reference them by ID across multiple passes using `templateOverride`. This reduces duplication and simplifies pass image management. * Create, list, and view image assets via the API * Supported image types: banner, logo, and thumbnail * Supports direct URL and base64 encoded uploads (PNG, JPG, WebP) * Batch operations now support image asset override columns Check out the guide here: [Image Assets](/api-reference/v1/image-assets/guide) **February 2026** ## New pass template builder We're rolling out a new pass template builder which includes a range of performance, design, and layout improvements. Major changes include: * Creating transit passes directly from the dashboard * Adding localisation to pass templates via the dashboard * Uploading more device-specific images for Apple and Google passes ![New pass template builder](https://mintcdn.com/passentry-0/1mlKidz_QLv_-t2a/changelog/images/new-pass-template-builder.png?fit=max\&auto=format\&n=1mlKidz_QLv_-t2a\&q=85\&s=62fd51881ad4b2aa49c5dbaa9ac19e2c) **December 2025** ## Support for external Apple Pass Type Identifier Certificates We've added support for external Apple Pass Type Identifier Certificates. This allows you to use your own Pass Type Identifier Certificates, rather using our internal certificates. Check out the guide here: [Adding an Apple Pass Certificate](/documentation/adding-an-apple-pass-certificate) **November 2025** ## Batch Pass Update via CSV in closed beta We've added support for batch pass update via CSV. Batch updates are scheduled to run at a specified time, and can be cancelled if needed. Check out the guide here: [Batch Pass Update](/documentation/bulk-operations/batch-pass-update/guide) ![Batch Update Index](https://mintcdn.com/passentry-0/1mlKidz_QLv_-t2a/changelog/images/pass-update-index.png?fit=max\&auto=format\&n=1mlKidz_QLv_-t2a\&q=85\&s=bdcaa8a1675ea1fd6b0f79668be26086) > [!WARNING] > > This feature is in **closed beta**.\ > Please contact PassEntry for access. **November 2025** ## Multi-Factor Authentication (MFA) for Dashboard We've added multi-factor authentication (MFA) for PassEntry dashboard accounts. ![MFA dashboard login](https://mintcdn.com/passentry-0/1mlKidz_QLv_-t2a/changelog/images/mfa-dashboard-login.png?fit=max\&auto=format\&n=1mlKidz_QLv_-t2a\&q=85\&s=119a3ddd1a5eb4ba424ba5cadb831e1c) ![MFA dashboard login](https://mintcdn.com/passentry-0/1mlKidz_QLv_-t2a/changelog/images/mfa-dashboard-login-2.png?fit=max\&auto=format\&n=1mlKidz_QLv_-t2a\&q=85\&s=cb8d16c4c10c02a634b13767daad0b72) ![MFA dashboard login](https://mintcdn.com/passentry-0/1mlKidz_QLv_-t2a/changelog/images/mfa-dashboard-login-3.png?fit=max\&auto=format\&n=1mlKidz_QLv_-t2a\&q=85\&s=f0216676afcd8db88963907e8a7dfe30) **October 2025** ## Resetting overridden pass template fields After using the `templateOverride` option on pass to override template design, you can now revert to the original template design by passing `null` to any previously overridden value. Check out the guide here: [Pass Template Override](/api-reference/v1/passes/update-pass#body-pass-template-override) **October 2025** ## Geofencing for Android Geofencing, previously available only on Apple Wallet, is now supported for Google Wallet passes. You can configure locations and enable geofencing for Android devices. **September 2025** ## Pass Value Localisation We've added support for localising pass field values. This is available in the pass create and update endpoints. For more information and usage examples, check out the [Translating Passes: Localisation](../documentation/passes/localisation/guide) Guide. > [!WARNING] > > This feature is in **closed beta** and currently only available on the API.\ > Please contact PassEntry for access. **September 2025** ## Pass Template Localisation We've added per‑field localisation so you can present pass content in your customers' preferred language without duplicating templates. You can now localise both field labels and values by providing translations under the optional `localisation` object (keyed by language codes like `en`, `fr`, `is`). The `defaultLanguage` defines the fallback, and responses only include localisation when translations exist. You can localise any subset of fields – untranslated fields automatically use the default language. For more information and usage examples, check out the [Translating Passes: Localisation](../documentation/passes/localisation/guide) Guide. > [!WARNING] > > This feature is in **closed beta** and currently only available on the API.\ > Please contact PassEntry for access. **August 2025** ## New Transit Pass type We've added a new transit pass type to the API. This allows you to create a pass that is used for transit. > [!WARNING] > > This is currently only available on the API.\ > Dashboard support is limited — preview and editing are restricted. Check out the guide here: [Transit Pass Template](/api-reference/v1/pass-templates/create-pass-template), [Transit Pass](/api-reference/v1/passes/create-pass) **August 2025** ## Pass Download Page - Localisation We've added support for localising the pass download page into up to 5 different languages. This is available in the pass download page editor under the 'Languages' section. **July 2025** ## Dynamic Barcode Values You can now make the barcode value dynamic by using the `%{passData.xyz}` syntax(e.g., `Hello, %{passData.extId}!`). Currently, only `%{passData.extId}` and `%{passData.uuid}` are supported. Check out the guide here: [Pass Barcode](/api-reference/v1/passes/create-pass#body-pass-barcode-value), [Pass Template Barcode](/api-reference/v1/pass-templates/create-pass-template#body-pass-template-barcode-value) **July 2025** ## Webhook Subscriptions API We've introduced a new Webhook Subscriptions API that allows you to programmatically manage your webhook endpoints and supports multiple webhook subscriptions. You can now: * Create, update, and delete webhook subscriptions * List all your active webhook subscriptions * Test webhook endpoints before going live * Rotate webhook secret keys for enhanced security Each webhook subscription can be configured to receive specific event types, making it easier to manage different integration endpoints. Check out the guide here: [Webhook Subscriptions API](/api-reference/v1/webhook-subscriptions/guide) **July 2025** ## Pass download page troubleshooting link We've added a new option to include a Help & FAQs link on a pass download page. When enabled, this link will redirect users to the troubleshooting URL you provide—making it easier for them to get assistance during the download process. This feature is available in the pass download page editor. **July 2025** ## Pass Template Fields API You can now use the `override_field_value` key to enforce the latest default values from a pass template.\ When this key is set to `true`, all existing pass field values will be overridden with the current defaults defined in the pass template. If the key is not provided, it defaults to `false`, which means existing field values will be preserved and not forcibly updated. > [!NOTE] > > This key is only available when updating a pass template. Check out the guide here: [Pass Template Fields API](/api-reference/v1/pass-templates/update-pass-template#body-pass-template-fields) **July 2025** ## Pass Templates API now in open beta We’ve opened up access to the Pass Templates API to all users. You can now use it to create, update, and list pass templates, as well as manage template settings. If you run into any issues or have feedback, feel free to reach out to [support@passentry.com](mailto:support@passentry.com). Read the guide to get started: [Pass Templates API](/api-reference/v1/pass-templates/create-pass-template) **July 2025** ## Pass Download Pages We've added a new feature that allows you to create multiple custom pass download pages for your organisation. A page can be associated with an entity or one or more pass templates. This feature is available in the pass download pages section of the dashboard. Check out the guide here: [Pass Download Pages](../documentation/passes/downloading-passes/guide) **June 2025** ## Pass Created Webhook We've added a new webhook for when a pass is created. This webhook is triggered when a pass is created via API, dynamic forms or any other method. **June 2025** ## Ability to filter passes by external ID prefix You can now filter passes by external ID prefix to group passes together by extId, if they share a common prefix text value. `extIdPrefix` Check out the guide here: [Passes API](/api-reference/v1/passes/list-passes) **June 2025** ## Pass Templates API released on closed beta We've added a new API for managing pass templates. This allows you to create, update, and delete pass templates, as well as manage pass template settings. This API is currently in closed beta, and is only available to select users. If you'd like to join the beta, please contact us at [support@passentry.com](mailto:support@passentry.com). Check out the guide here: [Pass Templates API](/api-reference/v1/pass-templates/create-pass-template) **May 2025** ## Support for different barcode types We've added support for different barcode types, including `code128`, `pdf417`, and `aztec`. You can now select the barcode type from a dropdown when creating a pass and set the default barcode type for a pass template on dashboard. ![Barcode template defaults](https://mintcdn.com/passentry-0/1mlKidz_QLv_-t2a/changelog/images/barcode-pass-template-default.png?fit=max\&auto=format\&n=1mlKidz_QLv_-t2a\&q=85\&s=38e844becb33d9a7f3e5a58a64b4af0b) **May 2025** ## Improved team access controls Admins can now invite and remove team members, no longer limited to the lead account. A new "member" role offers view-only access. ![Account Meatball menu](https://mintcdn.com/passentry-0/1mlKidz_QLv_-t2a/changelog/images/account-management-meatball-menu.png?fit=max\&auto=format\&n=1mlKidz_QLv_-t2a\&q=85\&s=9310013c32be0ac62664a20f33db5064) ![Change role modal](https://mintcdn.com/passentry-0/1mlKidz_QLv_-t2a/changelog/images/account-management-change-role-modal.png?fit=max\&auto=format\&n=1mlKidz_QLv_-t2a\&q=85\&s=d504a4c360207590bd20cd27c89fcd69) **April 2025** ## Additional app linking options **Apple** Passes now support multiple App Store links, displaying the first available app in the passholder's country. **Google** Added customisable display text for Google App links on the front of passes. **April 2025** ## Custom NFC source for passes and setting NFC defaults at template level When creating a pass via dashboard, you can now select the NFC source from a dropdown. This allows you to use a custom NFC source for your pass. You can also set the default NFC source for a pass template, which will be used when creating a new pass from that template. ![NFC template defaults](https://mintcdn.com/passentry-0/1mlKidz_QLv_-t2a/changelog/images/nfc-template-defaults.png?fit=max\&auto=format\&n=1mlKidz_QLv_-t2a\&q=85\&s=28f3aa3b8559e2609bb289ed36b29a31) **April 2025** ## Custom pass download success message We've added a new feature that allows you to customise the success message that is displayed when a pass is downloaded. This feature is available in the pass download page editor. **April 2025** ## Loyalty Info Now Visible on Scans We've added loyalty data to the `/scans` and `/scans/log` endpoints - meaning loyalty actions are now logged at the scan level and visible in the 'Scans' section on the dashboard. You can now validate a pass and manage loyalty in a single request, with support for `add`, `redeem`, `override`, and `stamp` operations. **April 2025** ## VTAP Automatic Reader Setup You can now set up your VTAP readers automatically via the PassEntry Dashboard - no need to access VTAP Cloud. The setup takes just a few minutes. Check out the guide here: [VTAP Automatic Reader Setup](../documentation/guides/vtap-reader/guide) **April 2025** ## Batches API You can now interact with batches via [API](/api-reference/v1/batches/guide). We currently support listing, cancelling, and viewing batches, as well as creating wallet messaging batches. **March 2025** ## Custom Pass Issuance Forms We've added a new feature that allows you to create custom pass issuance forms for your passes. This feature is available in the pass template editor. Check out the guide here: [Custom Pass Issuance Forms](/documentation/guides/custom-download-forms/guide) **March 2025** ## Dashboard Redesign We've redesigned the Dashboard to improve visibility, filtering, and performance. The update includes top-level stats, and enhanced Passes, Scans, and Messaging sections with detailed breakdowns and flexible date range controls (7 days, 1 month, 6 months, all time). We've also made performance improvements for faster loading. **February 2025** ## Auto-format notification links When sending a notification with a link, the link will now be formatted automatically so it is clickable on both Apple and Google, without needing to know the device type. Check out the guide here: [Notification links](../documentation/passes/notifications/guide) **October 2024** ## Third Party Validation We've added support for third-party validation. This allows you to use your own validation logic, such as a QR code scanner, to validate passes. Check out the guide here: [Third Party Validation](../documentation/guides/validation-options/guide) # Adding an Apple Pass Certificate Source: https://docs.passentry.com/documentation/adding-an-apple-pass-certificate Set up an Apple Pass Type Identifier and certificate in PassEntry to issue Apple Wallet passes, plus certificate expiration, renewal and pass grouping. ## Overview ### What is an Apple Pass Type Identifier? An Apple Pass Type Identifier is required to create and update Apple Wallet passes. Each pass must be associated with a valid Pass Type Identifier and certificate for cryptographic authentication. If the Pass Type Identifier or certificate is invalid, passes cannot be added to Apple Wallet or receive updates. ### Do I need to create an Apple Pass Type Identifier? PassEntry can issue Apple Pass Type Identifiers on behalf of clients who meet our eligibility criteria. This allows you to create and update Apple Wallet passes without managing your own Pass Type Identifier. If your use case requires you to manage your own Pass Type Identifier, follow the guide below. If you are unsure, please reach out to PassEntry via [support@passentry.com](mailto:support@passentry.com). > [!NOTE] > > Apple Guide: [Create Wallet identifiers and certificates](https://developer.apple.com/help/account/capabilities/create-wallet-identifiers-and-certificates/) ## Guide **1. Contact PassEntry to create an entity for your Pass Type Identifier** The first step will be to reach out to PassEntry to create an entity for your Pass Type Identifier. **2. Enroll in the Apple Developer Program** To create an Apple Pass Type Identifier and the corresponding certificate, you will need to enroll in the [Apple Developer Program](https://developer.apple.com/programs/enroll/) if you haven't already. > [!NOTE] > > Enrollment takes 1-2 business days to complete and costs \$99 per year. **3. Create an Apple Pass Type Identifier** 1. Login to your Apple Developer Portal and [Register a Pass Type ID](https://developer.apple.com/account/resources/identifiers/passTypeId/add/). 2. Enter a Description and Identifier (We recommend including your organisation name and vertical in the identifier. Eg: pass.com.passentry.giftcard) and click "Continue". 3. Confirm the submitted information and click "Register" to create the Pass Type Identifier. **4. Download your Certificate Signing Request (CSR)** In order to create a certificate, you will need to download a Certificate Signing Request (CSR). 1. Use the PassEntry API to download your CSR file. ```bash Download your CSR file theme={null} curl --location \ --request GET \ 'https://api.passentry.com/api/v1/entities/{YOUR_ENTITY_ID}/apple/certificate-signing-request' \ --header 'Authorization: Bearer {YOUR_API_KEY}' \ --header 'Accept: application/pkcs10' \ --output PassEntry.csr ``` 2. Save the CSR file to your computer. Check that the file is a valid CSR file by opening it in a text editor. It should contain the following: ```text theme={null} -----BEGIN CERTIFICATE REQUEST----- ... -----END CERTIFICATE REQUEST----- ``` **5. Create an Apple Pass Type Identifier Certificate** In order to create, distribute and update Apple Wallet passes, you will need to create a certificate, associated with your Pass Type Identifier. 1. From your Apple Developer Portal, navigate to the [Create a New Certificate](https://developer.apple.com/account/resources/certificates/list) page. 2. Select "Pass Type ID Certificate" from the dropdown under "Services" and click "Continue". 3. Enter your Pass certificate Name(optional) and select the Pass Type Identifier you created in the previous step, then click "Continue". 4. You will be prompted to upload your CSR file. Upload the CSR file you downloaded in the previous step and click "Continue". 5. Your certificate has been created and will display information about the certificate, such as the certificate ID and the expiration date of the certificate. > [!WARNING] > > At this point, it is important that you note down the expiration date of the certificate, as you will need to renew it before it expires. 6. Download the certificate and save it to your computer, this will be saved as `pass.cer` **6. Upload your certificate to PassEntry** Once you have downloaded your certificate, you will need to upload it to PassEntry. 1. Use the PassEntry API to upload your certificate file. ```bash Upload your certificate file theme={null} curl --location 'https://api.passentry.com/api/v1/entities/{YOUR_ENTITY_ID}/apple/certificate' \ --header 'Authorization: Bearer {YOUR_API_KEY}' \ --form 'certificate=@"{PATH_TO_YOUR_APPLE_CERTIFICATE}"' ``` 2. Your certificate has been uploaded and will display information about the certificate, such as the Pass Type Identifier\*\* \*\*and the expiration date of the certificate. ## Considerations ### Apple Developer Program Access It is critical you ensure persistent access to your Apple Developer Program account. If you lose access to your account, you will not be able to create and update Apple Wallet passes. ### Certificate Expiration Apple Pass Type Identifier certificates are valid for 1 year. Apple will notify you via email when your certificate is about to expire, and you will need to renew it by creating a new certificate. > [!WARNING] > > You are responsible for renewing your certificate before it expires. If your certificate expires, you will no longer be able to create and update Apple Wallet passes. ### Certificate Renewal To renew your certificate, you will need to create a new certificate. Follow the guide above, from the step "Create an Apple Pass Type Identifier Certificate" to create a new certificate. Then proceed to the step "Upload your certificate to PassEntry" to upload the new certificate. This will replace the certificate in PassEntry with the new certificate. ### Pass Grouping Passes with the same Pass Type Identifier will be grouped together in the Apple Wallet. If you want to separate passes in the wallet, these will need to have a different Pass Type Identifier. In PassEntry's architecture, each Pass Type Identifier is associated with an entity, so you will need to create a new entity for each additional Pass Type Identifier. # Batch wallet notifications and logging Source: https://docs.passentry.com/documentation/bulk-operations/batch-notifications/guide Send wallet notifications in bulk using pass IDs, segments, or CSV uploads, and track delivery status and errors for large-scale messaging. **Batch Wallet Notifications** let you send the same wallet message to large groups of pass holders in a single operation. Use batches for announcements, reminders or campaign messages where you need to notify many recipients at once. You can define recipients using **pass IDs, dynamic segments or CSV uploads**, making this feature suitable for one-off campaigns and large data sets **without requiring API integration.** Let’s explore each option in more detail below. > [!NOTE] > > This guide covers creating and managing batch wallet notifications using the PassEntry Dashboard. If you want to **send batch notifications programmatically** or integrate them into automated workflows, see the > [Batch Wallet Messaging API documentation](/api-reference/v1/batches/create-wallet-messaging-batch). *** ## Creating a wallet notifications batch [Tutorial: Intro to Batch Wallet Messaging](https://www.youtube.com/embed/aqUD2piCw-8?si=-Il-hjUGz-ZwbIhN) > **Video transcript** > > In this video, we'll walk you through how to send a batch message from the PassEntry dashboard. To get started navigate to the messaging section using left-hand navigation bar. > Click the "New Message" then review and accept the terms and conditions. > Next, give your message batch a title, this helps you stay organised. You can also add tags to group batches together for specific campaigns or purposes, > which we'll cover in more detail in another video. > Now its time to write your message. For this example: "Enjoy 10% off at the bar from 7pm tonight." > once your message is ready you can schedule it for a specific time. in this case we're sending it at 4 pm giving attendees plenty of time to grab a drink before the event kicks off. > The final step is selecting your audience. PassEntry gives you several targeting options, > which we'll cover in depth in another video. You can send to individual passes by UUID or external ID, filter by entity, pass template or group tags, > or upload a CSV if you've already segmented your audience externally. For this example we're using Pass Template to message all attendees of a fictional event. > Click Send Message, confirmation the number of passes in the pop-up and hit send. > PassEntry will validate each pass and schedule delivery. From the message menu you can review the details, or if needed, override the scheduled time and send immediately. > That's a quick introduction to batch messaging. Check out the other videos in this series to explore more of the messaging features. Head to the **Messaging** section and click on the **New Message** button. ![](https://mintcdn.com/passentry-0/HmGZWgR5hl_EFpPJ/documentation/bulk-operations/batch-notifications/images/messaging-new-message.png?fit=max\&auto=format\&n=HmGZWgR5hl_EFpPJ\&q=85\&s=03f4461d54e2d7a385b7e73dd3b664b3) Complete the batch notifications form step-by-step: ![](https://mintcdn.com/passentry-0/HmGZWgR5hl_EFpPJ/documentation/bulk-operations/batch-notifications/images/new-message-form-start.png?fit=max\&auto=format\&n=HmGZWgR5hl_EFpPJ\&q=85\&s=4c99aa2714558d9981194bec729eb6cf) 1. **Batch Name**. Optional. Enter a unique name for the notification batch. This helps you identify and manage batches easily in the future. If you don't include a name, PassEntry automatically names the batch "Batch \[DateTime]". 2. **Tag**. Optional. Add a custom label to group and track your notification batches. 3. **Message**. Required. Write the message you want to send to your recipients. You can use variables to customise the message. Variables are placeholders like `%{variable_name}` that PassEntry replaces with specific values for each recipient. Click **Show Examples** for help. 4. **Send At**. Required. Choose a date, time, and timezone to schedule the notifications. You can schedule messages to be sent anytime between 1 hour and 365 days in advance, making it convenient to plan campaigns. ### Recipients There are three ways to define recipients for your message: [YouTube video player](https://www.youtube.com/embed/KmdZGm8NSPg?si=2rxq0koQFfAzmie2) > **Video transcript** > > In this video, we'll walk you through the powerful pass filtering options available when sending batch messages through the dashboard. > To get started, we've created a message batch and assigned it a name and tag. > We've added our message content and scheduled it to send at 4pm on the day of the event. > Now let's explore how to control exactly who receives your message. The first option is filtering by individual pass. > If you need to target a specific user — or a small selection of users — you can enter a UUID or External Pass ID directly into the recipient field. > You can add up to ten IDs at a time, giving you precise, targeted control. This is ideal for one-to-one or high-value communications. > When you need to communicate at scale, we offer several broader filtering options. The first is by entity. Selecting this will send your message to every pass holder within that entity. > If no dropdown is present, the message will go to your entire pass holder base. The second option is by pass template. This allows you to target all pass holders belonging to a specific template. For example, > if you have templates set up for individual events or artists — such as James Blake or Yasmin Lacey — you can direct your message exclusively to the pass holders for that template. > Our most flexible option is filtering by group tag. Group tags allow you to segment your pass holders, giving you granular control over your audience. > For instance, combining a persona tag of Regular with a source tag of Instagram will send messages to all passes tagged with those attributes in this case seven. > Switching to a VIP tag returns eight. The more consistently you tag your passes, the more powerful and precise your segmentation becomes, making this an exceptionally effective tool for targeted messaging campaigns. > Finally, for maximum flexibility, you also have the option to upload a CSV file — a feature we'll cover in detail in a separate video. > This allows you to provide a custom list of pass IDs and individualise messages for each recipient using variables Whether you're sending a single targeted message or broadcasting to thousands, > the dashboard gives you the tools to reach exactly the right audience, every time. For more on messaging check out the other videos in this series. * **Pass IDs**. Manually enter the pass UUID or external ID for each recipient, along with any variables needed for the message. You can add up to 10 recipients in the Dashboard, or up to 500 when using the API. ![](https://mintcdn.com/passentry-0/HmGZWgR5hl_EFpPJ/documentation/bulk-operations/batch-notifications/images/manual.png?fit=max\&auto=format\&n=HmGZWgR5hl_EFpPJ\&q=85\&s=9d038159cc8e01f180d0459de3389f9d) * **Pass Filtering**. Send messages to everyone within a specific segment. You can target all passes in an entity, or filter by **pass template**, **group tags**, or **both combined**. Combining them targets passes on that template that also carry at least one of the selected tags. The recipient counter displays how many users you're targeting. Note that variables cannot be used in messages when using pass filtering. ![](https://mintcdn.com/passentry-0/HmGZWgR5hl_EFpPJ/documentation/bulk-operations/batch-notifications/images/pass-filtering.png?fit=max\&auto=format\&n=HmGZWgR5hl_EFpPJ\&q=85\&s=b718264251abe7ec7542deced6d91cc1) > [!TIP] > > To filter by group tag, your passes need tags applied first. You can set group tags in any of these ways: > > * **API**: Include `groupTag` (single tag) or `groupTags` (array of tags) in the request body when [creating](/api-reference/v1/passes/create-pass) or [updating](/api-reference/v1/passes/update-pass) a pass. > * **Bulk CSV**: Add a `group_tags` column (comma-separated values) in [batch pass creation](/documentation/bulk-operations/batch-pass-creation/guide) or [batch pass update](/documentation/bulk-operations/batch-pass-update/guide). > * **Custom pass issuance forms**: Append `?groupTag=` or `?groupTags=tag1,tag2` to the form URL, or map a form field to **none** so its value is applied as a group tag. See [Custom Pass Issuance Forms](/documentation/guides/custom-download-forms/guide). * **CSV Upload**. Upload a CSV file containing recipient data (up to 10 MB). The required column is `pass_id`, which accepts either the pass UUID or external ID. If your message uses variables, add a column for each variable, with the column name matching the variable name in the message. > **Example:** For a message like **"Hello, %\{name%}. Your ticket number is %\{ticket\_number%}"**, your CSV file should include columns named `name` and `ticket_number`: ```csv sample_recipients_wallet.csv theme={null} pass_id,name,ticket_number 6CA1339D,John Doe,ABC123 E7178F47,Jane Smith,XYZ789 ``` ![](https://mintcdn.com/passentry-0/HmGZWgR5hl_EFpPJ/documentation/bulk-operations/batch-notifications/images/csv-upload.png?fit=max\&auto=format\&n=HmGZWgR5hl_EFpPJ\&q=85\&s=3da07c9038e7ac9e1ef1d8cb3148aab3) Once you’re satisfied with your setup, double-check the message and recipient details, then click **Send Message** to confirm. ## Message history: tracking and managing your batches Once you submit your first message batch, you can track all your batches on the **Message History** page, which is divided into two tabs: **Message Batches** and **Scheduled Message Batches**. * **Main Information**: Each batch displays key details such as the batch name, message text and type, status, and completion time. * **Error Handling**: If any errors occurred, you can download an error CSV directly from this page. ![](https://mintcdn.com/passentry-0/HmGZWgR5hl_EFpPJ/documentation/bulk-operations/batch-notifications/images/messaging-history.png?fit=max\&auto=format\&n=HmGZWgR5hl_EFpPJ\&q=85\&s=ce7d268cc87b34630489481e0d1aeaac) * **Batch Details**: Click the circular arrow icon to open full batch details. From there, you can download a detailed delivery report, broken down by recipient and updated regularly while the batch sends. ![](https://mintcdn.com/passentry-0/HmGZWgR5hl_EFpPJ/documentation/bulk-operations/batch-notifications/images/more-info.png?fit=max\&auto=format\&n=HmGZWgR5hl_EFpPJ\&q=85\&s=733a3c7e3a1c52710f593fd05d3c9ef6) * **Cancelling a scheduled batch**: You can cancel a scheduled batch at any time before the scheduled send time. The one-hour scheduling threshold does not apply when cancelling a scheduled batch. ![](https://mintcdn.com/passentry-0/HmGZWgR5hl_EFpPJ/documentation/bulk-operations/batch-notifications/images/scheduled.png?fit=max\&auto=format\&n=HmGZWgR5hl_EFpPJ\&q=85\&s=55b01c393c345d168d8f5c42941c22f4) *** ## FAQ ### When should I use batch notifications instead of sending messages individually? Use batch notifications when you need to send the same message to many recipients at once. Batches are more efficient, easier to track, and better suited for campaigns or large-scale updates. ### Can I personalise messages in batch notifications? Yes, but only when using pass IDs or CSV uploads. Personalisation variables are not supported when targeting recipients via pass filtering. ### What happens if some messages fail to send? If any errors occur during delivery, you can download an error CSV from the Message History page to see which recipients failed and why. ### Can I cancel a scheduled batch? Yes. You can cancel a scheduled batch at any time before the send time. The usual one-hour scheduling limit does not apply when cancelling. *** That’s everything you need to know to get started with Batch Wallet Notifications. We’d love to hear your thoughts – if you have any feedback or run into issues, let us know so we can keep improving! # Batch pass creation: issue passes at scale via CSV upload Source: https://docs.passentry.com/documentation/bulk-operations/batch-pass-creation/guide Create passes at scale in the PassEntry Dashboard by uploading a CSV, mapping template fields, and reviewing batch results to fix any errors. **Batch Pass Creation** allows you to create large numbers of passes in a single operation using a CSV file. Use this approach when you need to issue passes at scale, such as during migrations, bulk onboarding or campaign launches. > [!NOTE] > > This guide covers creating passes via CSV upload in the PassEntry Dashboard. If you want to **create passes programmatically** or integrate bulk issuance into > automated workflows, see the [Batch Pass Creation API documentation](/api-reference/v1/batches/create-passes-batch). ## Creating a batch via CSV Go to the **Bulk Operations** section and click **Create via CSV**. Then create a new batch. Optionally enter a batch name, and select the pass template you want the passes to be created under. ![](https://mintcdn.com/passentry-0/HmGZWgR5hl_EFpPJ/documentation/bulk-operations/batch-pass-creation/creation-form.png?fit=max\&auto=format\&n=HmGZWgR5hl_EFpPJ\&q=85\&s=1f9a5563ae5415058566571abefa0264) **Sample CSV** You can download a sample CSV from the batch creation form in the Dashboard. Select a pass template first: the sample CSV download link appears once you select a template, and the file includes the columns specific to your chosen template. ## Configuration > [!WARNING] > > Ensure the CSV is formatted in UTF-8 encoding. ### Fixed headers For more information on the functionality of each field, please refer to the [API Reference](../../../api-reference/v1/passes/create-pass).
Column Name Description Required? Example Type
ext\_id External ID for the pass Yes Pass12345 Text (Any)
`group_tags` Comma separated tags to group passes No summer\_offer\_24 Text (Any)
`nfc_enabled` Boolean to enable NFC No true Boolean (true or false)
`nfc_source` Source of NFC data No extId Text (extId or uuid or custom)
`nfc_custom_value` Custom value for NFC, `nfc_source` must be set to custom No summer\_20\_off Text (any)
`barcode_enabled` Boolean to enable barcode No true Boolean (true or false)
`barcode_type` Type of barcode No `qr` Text (`qr`, `code128`, `aztec` or `pdf417`)
`barcode_source` Source of barcode No extId Text (extId, uuid or custom)
`barcode_value` Value for the barcode, `barcode_source` must be set to custom No abcd12345 Text (Any)
`barcode_display_text` Boolean to display barcode value as text No true Boolean (true or false)
`expires_at` Expiration date and time for the pass No 2024-08-14T18:12:11Z Datetime in iso8601 format
`override_background_color` Override the background colour of the pass. [Restrictions apply depending on device and template type](../../../api-reference/v1/passes/create-pass#body-pass-template-override) No #000000 Text (hex colour code)
`override_label_color` Override the label colour of the pass. [Restrictions apply depending on device and template type](../../../api-reference/v1/passes/create-pass#body-pass-template-override) No #FFFFFF Text (hex colour code)
`override_text_color` Override the text colour of the pass. [Restrictions apply depending on device and template type](../../../api-reference/v1/passes/create-pass#body-pass-template-override) No #FFFFFF Text (hex colour code)
`override_banner_image_asset_id` UUID of a processed [Image Asset](/api-reference/v1/image-assets/guide) to use as the banner image No f47ac10b-58cc-4372-a567-0e02b2c3d479 Text (UUID)
`override_logo_image_asset_id` UUID of a processed [Image Asset](/api-reference/v1/image-assets/guide) to use as the logo image No f47ac10b-58cc-4372-a567-0e02b2c3d479 Text (UUID)
`override_thumbnail_image_asset_id` UUID of a processed [Image Asset](/api-reference/v1/image-assets/guide) to use as the thumbnail image (generic passes only) No f47ac10b-58cc-4372-a567-0e02b2c3d479 Text (UUID)
> [!NOTE] > > Template override columns require template overrides to be enabled for your organisation. Image assets must be uploaded and fully processed before they can be referenced by UUID. See the [Image Assets guide](/api-reference/v1/image-assets/guide) for details. ### Dynamic headers Each template can have custom fields, which you identify and set via dynamic headers. When you create a template, you set an `ID` for each field. Target that field in your CSV by including the prefix `passfield_` followed by your template ID. Example: In your pass template, you've chosen to display Field 1 as "Customer Name", and given it the `ID` of "name". Then, for each row in your CSV, you've entered a different name, to correspond with the data downloaded from your system. In which case, your CSV would need to have a row called `passfield_name` ![](https://mintcdn.com/passentry-0/HmGZWgR5hl_EFpPJ/documentation/bulk-operations/batch-pass-creation/dynamic-field-id-example.png?fit=max\&auto=format\&n=HmGZWgR5hl_EFpPJ\&q=85\&s=ee44747c386774a2f1e38c6d596e7c5e) ### Special characters and markdown CSV files that include special characters (commas, quotes, or line breaks) — or embedded HTML or markdown content — need careful formatting so fields parse reliably. Follow these rules: * Commas (`,`): Wrap the entire field in double quotes (`"`). * Line breaks (`\n`): Wrap the entire field in double quotes (`"`). * Double quotes (`"`) inside fields: Use double quotes around the field, and escape inner quotes by doubling them (`""`). * Markdown or HTML: Use double quotes around the full field, and use single quotes inside HTML attributes to avoid quote conflicts, or escape double quotes if needed. For example: | Desired Field Content | CSV-Compliant Value | | - | - | | London, UK | "London, UK" | | London\nUK (line break) | "London\nUK" | | `Link` | `"Link"` | | "Quoted Text" | """Quoted Text""" | | `"Link"` | `"""Link"""` | This ensures that the CSV parser handles the special characters correctly and the markdown renders as expected. ## Results Once you've uploaded your CSV, you can track the progress of your batch on the Passes page. ![](https://mintcdn.com/passentry-0/HmGZWgR5hl_EFpPJ/documentation/bulk-operations/batch-pass-creation/pass-creation-index.png?fit=max\&auto=format\&n=HmGZWgR5hl_EFpPJ\&q=85\&s=499f538b8653a93b4d3a1d29fed612fe) The batch appears in the "Batches" section. Click it to see the batch progress. If any rows fail, you can download a CSV of the errors. Once you've fixed them, re-upload the CSV to try again. Once the batch is complete, you can download a CSV of the passes created in the following format: | ext\_id | uuid | download\_url | | - | - | - | | Pass12345 | 1234567890 | [https://download.passentry.com/download?pass=1234567890](https://download.passentry.com/download?pass=1234567890) | | Pass12346 | 1234567891 | [https://download.passentry.com/download?pass=1234567891](https://download.passentry.com/download?pass=1234567891) | ### Retrieving download URLs via the API If you created the batch through the [Create Passes Batch](/api-reference/v1/batches/create-passes-batch) API rather than the dashboard, follow the same result flow programmatically: 1. Poll [Show Batch](/api-reference/v1/batches/show-batch) with the batch `id` until `status` is `successful`. 2. Read the `resultCsvUrl` field from the response and download the CSV. 3. Each row contains `ext_id`, `uuid`, and `download_url` for the created pass. Download URLs follow the format `https://download.passentry.com/download?pass={uuid}`, so you can also construct them directly from a pass UUID returned by [Show Pass](/api-reference/v1/passes/show-pass) under `data.attributes.downloadUrl`. ## FAQ ### When should I use batch pass creation instead of the API? Use CSV batch creation for one-off or infrequent bulk operations, such as migrations or campaign launches. For recurring or automated workflows, use the standard pass creation API to issue passes programmatically. ### What happens if some rows in my CSV fail? If any rows fail, you can download an error CSV showing which rows failed and why. You can fix the errors and re-upload the CSV to retry only the failed passes. ### Is there a limit to how many passes I can create in one batch? The maximum batch size depends on your plan and system limits. Large CSV files are supported, but extremely large batches may take longer to process. # Batch Pass Update Source: https://docs.passentry.com/documentation/bulk-operations/batch-pass-update/guide Update existing passes in bulk with CSV uploads in the PassEntry Dashboard, map template fields, track batch progress and resolve row errors. **Batch Pass Update** allows you to modify existing passes in bulk using a CSV file. Use this feature when you need to apply the same type of change to many passes at once, such as updating expiration dates, modifying pass fields, deactivating passes or adjusting NFC and barcode settings. > [!NOTE] > > This guide covers updating passes via CSV upload in the PassEntry Dashboard. > For programmatic bulk updates, see the [Batch Pass Update API documentation](/api-reference/v1/batches/update-passes-batch). > For recurring or real-time per-pass updates, use the standard [Update Pass API](/api-reference/v1/passes/update-pass). ## Updating passes via CSV Go to the **Bulk Operations** section and click **Update via CSV**. Then create a new batch. Optionally enter a batch name, and select the pass template you want the passes to be updated under. ![](https://mintcdn.com/passentry-0/HmGZWgR5hl_EFpPJ/documentation/bulk-operations/batch-pass-update/update-form.png?fit=max\&auto=format\&n=HmGZWgR5hl_EFpPJ\&q=85\&s=33830bc874b5c98ae3cee2dd8cbc6912) **Sample CSV** You can download a sample CSV from the batch update form in the Dashboard. Select a pass template first: the sample CSV download link appears once you select a template, and the file includes the columns specific to your chosen template. ## Configuration > [!WARNING] > > Ensure the CSV is formatted in UTF-8 encoding. ### Fixed headers For more information on the functionality of each field, please refer to the [API Reference](../../../api-reference/v1/passes/update-pass).
Column Name Description Required? Example Type
`pass_id` UUID or external ID for the pass Yes 1234567890 Text (uuid or extId)
`group_tags` Comma separated tags to group passes No summer\_offer\_24 Text (Any)
`nfc_enabled` Boolean to enable NFC No true Boolean (true or false)
`nfc_source` Source of NFC data No extId Text (extId or uuid or custom)
`nfc_custom_value` Custom value for NFC, `nfc_source` must be set to custom No summer\_20\_off Text (any)
`barcode_enabled` Boolean to enable barcode No true Boolean (true or false)
`barcode_type` Type of barcode No `qr` Text (`qr`, `code128`, `aztec` or `pdf417`)
`barcode_source` Source of barcode No extId Text (extId, uuid or custom)
`barcode_value` Value for the barcode, `barcode_source` must be set to custom No abcd12345 Text (Any)
`barcode_display_text` Boolean to display barcode value as text No true Boolean (true or false)
`expires_at` Expiration date and time for the pass No 2024-08-14T18:12:11Z Datetime in iso8601 format
`pass_action` Action to perform on the pass No deactivate Text (deactivate or reactivate)
`override_background_color` Override the background colour of the pass, or `null` to revert to template default. [Restrictions apply depending on device and template type](../../../api-reference/v1/passes/update-pass#body-pass-template-override) No #000000 Text (hex colour code or `null`)
`override_label_color` Override the label colour of the pass, or `null` to revert to template default. [Restrictions apply depending on device and template type](../../../api-reference/v1/passes/update-pass#body-pass-template-override) No #FFFFFF Text (hex colour code or `null`)
`override_text_color` Override the text colour of the pass, or `null` to revert to template default. [Restrictions apply depending on device and template type](../../../api-reference/v1/passes/update-pass#body-pass-template-override) No #FFFFFF Text (hex colour code or `null`)
`override_banner_image_asset_id` UUID of a processed [Image Asset](/api-reference/v1/image-assets/guide) to use as the banner image, or `null` to remove No f47ac10b-58cc-4372-a567-0e02b2c3d479 Text (UUID or `null`)
`override_logo_image_asset_id` UUID of a processed [Image Asset](/api-reference/v1/image-assets/guide) to use as the logo image, or `null` to remove No f47ac10b-58cc-4372-a567-0e02b2c3d479 Text (UUID or `null`)
`override_thumbnail_image_asset_id` UUID of a processed [Image Asset](/api-reference/v1/image-assets/guide) to use as the thumbnail image (generic passes only), or `null` to remove No f47ac10b-58cc-4372-a567-0e02b2c3d479 Text (UUID or `null`)
> [!NOTE] > > Template override columns require template overrides to be enabled for your organisation. Image assets must be uploaded and fully processed before they can be referenced by UUID. See the [Image Assets guide](/api-reference/v1/image-assets/guide) for details. > > Set any override column to the literal string `null` to remove the override and revert to the template default. ### Dynamic headers Each template can have custom fields, which you identify and set via dynamic headers. When you create a template, you set an `ID` for each field. Target that field in your CSV by including the prefix `passfield_` followed by your template ID. > [!NOTE] > > You only need to include columns for the fields you wish to update. Any columns omitted from the CSV remain unchanged on the pass. The only required column is `pass_id`. Example: In your pass template, you've chosen to display Field 1 as "Customer Name", and given it the `ID` of "name". Then, for each row in your CSV, you've entered a different name, to correspond with the data downloaded from your system. In which case, your CSV would need to have a row called `passfield_name` ![](https://mintcdn.com/passentry-0/HmGZWgR5hl_EFpPJ/documentation/bulk-operations/batch-pass-update/dynamic-field-id-example.png?fit=max\&auto=format\&n=HmGZWgR5hl_EFpPJ\&q=85\&s=2d4e344dc2436be8cd00466ba48ef39b) ### Special characters and markdown CSV files that include special characters (commas, quotes, or line breaks) — or embedded HTML or markdown content — need careful formatting so fields parse reliably. Follow these rules: * Commas (`,`): Wrap the entire field in double quotes (`"`). * Line breaks (`\n`): Wrap the entire field in double quotes (`"`). * Double quotes (`"`) inside fields: Use double quotes around the field, and escape inner quotes by doubling them (`""`). * Markdown or HTML: Use double quotes around the full field, and use single quotes inside HTML attributes to avoid quote conflicts, or escape double quotes if needed. For example: | Desired Field Content | CSV-Compliant Value | | - | - | | London, UK | "London, UK" | | London\nUK (line break) | "London\nUK" | | `Link` | `"Link"` | | "Quoted Text" | """Quoted Text""" | | `"Link"` | `"""Link"""` | This ensures that the CSV parser handles the special characters correctly and the markdown renders as expected. ## Tracking and managing your batches Once you've uploaded your CSV, you can track the progress of your batch. The progress bar updates in real time, and you can see the number of passes processed, pending, and total. If the progress bar is not updating, please refresh the page. ![](https://mintcdn.com/passentry-0/HmGZWgR5hl_EFpPJ/documentation/bulk-operations/batch-pass-update/pass-update-index.png?fit=max\&auto=format\&n=HmGZWgR5hl_EFpPJ\&q=85\&s=c28b82a369fe4c587cbc0ab933dbaacb) The batch appears in the "Batches" section. Click it to see the batch progress. * **Main Information**: Each batch displays key details such as the batch name, template name, status, and scheduled time. * **Cancelling a scheduled batch**: You can cancel a scheduled batch at any time before the scheduled update time. * **Rescheduling a batch**: To reschedule an existing batch, cancel it, and create a new batch with the desired scheduled time. * **Updating a batch ahead of schedule**: You can update a batch ahead of schedule by clicking the "Update now" button. PassEntry then processes the updates immediately. ## Error handling If any rows fail, you can download a CSV of the errors. Once you've fixed them, re-upload the CSV to try again. ## FAQ ### Will this overwrite all fields on my passes? No. PassEntry updates only the columns included in your CSV. Any fields omitted from the CSV remain unchanged on the pass. ### When should I use batch updates instead of the Update Pass API? Use batch updates for one-off or infrequent bulk changes, such as migrations or campaign-wide updates. For real-time or recurring updates, use the Update Pass API. ### What happens if some rows fail to update? If any rows fail, you can download an error CSV showing which passes failed and why. After fixing the errors, you can re-upload the CSV to retry. # Custom Pass Issuance Forms Source: https://docs.passentry.com/documentation/guides/custom-download-forms/guide Design customer-facing pass issuance forms on a pass template so users can enter their details and download passes themselves, without an API integration. Use **custom pass issuance forms** when you want customers to create and download passes themselves, for example during onboarding, promotions, registrations or in-store sign-ups, without building a custom integration. ## Getting started This feature is available when **creating or editing a pass template**. Enable it with the following toggle: ![](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/custom-download-forms/images/custom-download-form-toggle.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=99b59fd09fb06ae5dd7e2e4ed5e4ceee) ## Previewing your custom form You can preview your form in real time using the preview component while creating or editing a pass template with custom forms enabled. This feature displays the URL, design elements, and form fields for both mobile and desktop browsers. ![](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/custom-download-forms/images/preview-form.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=54db2053932253acea7cd17c9c78669a) ## Designing your custom form Once you enable the custom pass issuance form for your template, you will see several fields that allow you to personalise the form to your needs: **Form Name** - This is a required field that serves as an identifier for your form. It must be a **unique**, **dash-separated** string, as PassEntry uses it in the URL of your live form. **Header Image** - The logo displayed at the top of your form. By default, this is the logo from the pass template. **Favicon Logo** - The favicon logo displayed in the browser tab when viewing your form. If not specified, it defaults to the icon logo from the pass template. **Background Colour** - Defines the background colour of your form. **Component Fill Colour** - Sets the colour of the form components. By default, this matches the pass background colour from the pass template. **Font Colour** - Determines the colour of the text displayed on your form. By default, this matches the title font colour from the pass template. **Button Background Colour** - Defines the colour of the button on your form. **Button Text Colour** - Determines the colour of the text displayed on the button. **Button Text** - The text displayed on the button. ### Form fields These form fields define the structure of your form. Click "Edit" to expand the fields, enabling you to add, remove, reorder, or configure them as needed. When designing a custom form, you can choose between two field types: **input** and **dropdown**. Each type includes various sub-fields that can be customised to suit your requirements. By default, the form fields are pre-populated with input fields corresponding to the active fields on your pass template. ![](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/custom-download-forms/images/form-fields.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=a9eba878bf66ca16a2682273d805b2d6) #### Input fields Input fields allow users to enter text or numeric information. The template field, label, and validation rules can be customised to suit your requirements. ![](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/custom-download-forms/images/input-field.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=81ce8bef0731d277fa502201e9f488b8) **Ext ID** - This is an optional toggle which can be enabled for one of your form fields to **uniquely identify** a pass created by the form. **Template field** - This corresponds to the field on your pass template that user input on your form populates. **Label** - This is the label displayed on your custom form for a given field. **Validation type** - Choose from the pre-configured validation options (**Phone**, **Email**, **URL**), which apply the relevant validation to the form field. A **custom** option lets you enter custom regex to apply to the field. **Custom error message** - You can also enter a custom error message that appears if the user input doesn’t meet the requirements. It defaults to `Please enter a valid
`. For example: ![](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/custom-download-forms/images/custom-validation.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=75ebbd5823fe3190adc84c6b950f06bb) #### Date fields Date fields allow users to enter a date to be applied to their pass with specific date formatting. ![](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/custom-download-forms/images/date-input.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=13d18ed0e0e0f3ffbcbe71165ebe2fbe) **Date Format** - Set the format in which a user's input appears on their pass once created. The options are `dd/mm/yyyy`, `yyyy/mm/dd`, `dd/mm/yy`, and `Month Day, Year`. #### Dropdown fields Dropdown fields provide a list of options for users to select from. ![](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/custom-download-forms/images/dropdown-field.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=00b7c930880bf8bea21c98a0f46b265f) **Template field** - This behaves similarly to input fields, but with an additional option to select **none**. Instead of mapping the form input to a pass template field, **choosing none applies the value as a group tag to the created pass**, enabling effective segmentation and filtering of your passes. **Placeholder** - This is a customisable text placeholder that appears when no dropdown values have been selected. **Dropdown options** - These are the options displayed for this dropdown field. Click "Edit" to create, update, or delete your options. #### Custom query parameters You can also add custom query parameters to your form. This lets you pass additional data to the form to populate the pass. ![](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/custom-download-forms/images/custom-query-params.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=8f5d3e2436f5d89b3c6c1448350085ba) Here you can configure required and optional query parameters for your pass template fields that can be handled by your form to populate the pass without user input. An example URL would be `https://form.passentry.com/form-name?label1=value1&label2=value2`, which would populate the pass fields with the values for `label1` and `label2` respectively. If you create a custom query parameter for a template field that also has a corresponding form field (see [Form fields](#form-fields)), the user input provided to the form field takes precedence. You can also pass the optional `groupTag` or `groupTags` parameter to your pass form. PassEntry applies the provided values as group tags to the created pass for segmentation, for example `https://form.passentry.com/form-name?groupTag=example-group-tag` or `https://form.passentry.com/form-name?groupTags=example-group-tag1,example-group-tag2`. ## Publishing your form Once you have finished designing your form, PassEntry saves your changes when you save your pass template. The updates are immediately visible at the address shown in the preview component. In addition to sharing the form URL with your customers directly, you can also create a QR code that links to your form to display on your website or in a physical location. Multiple free QR code generators are available online. We recommend a provider that enables scan tracking so you can monitor user engagement. ## FAQ ### Can I create a form without any fields? Yes, you can create a form without any fields. With this configuration, PassEntry **automatically generates a pass** when a user visits your form URL. Make sure you **set default values** for the fields on the corresponding pass template, as there is no user input. ### Where can I find my form once it's live? The URL for your form is [https://form.passentry.com/form-name](https://form.passentry.com/form-name). You can also find and copy this URL from the form preview component in the pass template editor. ### How can I turn off my form? You can turn off your form by unchecking the **Enable pass issuance form** toggle in the pass template editor. This disables public access to the form while keeping its design saved, so you can re-enable it at any time. # PassEntry Reader App Configuration Guide Source: https://docs.passentry.com/documentation/guides/passentry-reader-app/guide Learn how to set up and configure the PassEntry Reader app to scan, validate and redeem Apple Wallet and Google Wallet passes using NFC or QR codes, including reader creation, login and available reader modes. Use the PassEntry Reader app to scan and validate passes at the point of entry, checkout or redemption using NFC or QR codes on supported devices. ## Getting Started 1. **Download the “PassEntry NFC/QR Pass Scanner” app** to your chosen device. The app is available on: * **Apple App Store** * **Google Play Store** * **Sunmi App Store** * **PAX MAXSTORE** * **Urovo App Store** * **More coming soon** 2. **Create your Reader on the PassEntry Dashboard** (instructions below). 3. **Login on your device** using the credentials generated during setup (details below). *** ## Create Your Reader on the PassEntry Web Dashboard 1. Navigate to the [**Readers**](https://app.passentry.com/readers) tab on the left side of the PassEntry Web Dashboard. 2. Click **Add Reader** in the top right corner. 3. If you have multiple entities, select the relevant entity for this reader in the top-left corner. 4. Enter the following: * A name for the reader. * A unique identifier (this will be used to sign in). 5. Select which features you want your reader to have: * **Scan Mode**: For ticketing and stamp cards. * **Redeem Mode**: For redeeming points with loyalty passes. * **Adding Mode**: For adding points with loyalty passes. * **QR Mode**: Adds camera functionality to scan QR passes as well as NFC. 6. Enter a memorable password. 7. Click **Save** to create the reader. ![](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/passentry-reader-app/images/reader-create.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=684bc1d093a41d08b09ca3fe72afd90d) *** ## Login on Your Device Using These Credentials 1. Open the **PassEntry NFC/QR Pass Scanner** app on your device. 2. Enter the **Reader ID** and **Password** created in the previous step. ![](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/passentry-reader-app/images/reader-login.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=7d4d2054d8061a7050433622801a7fef) *** ## Using the Reader Modes ### **Scan Mode** * When logged in and scan mode is enabled, the **Scan tab** will appear in the bottom navigation bar. * Use this for: * **General ticketing**: Checks for a valid pass. * **Stamp cards**: Each tap increments the user's stamp count. * If QR-code scanning is enabled, toggle the **camera mode** using the switch in the top-right corner. ![](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/passentry-reader-app/images/scan-mode.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=b83522f3a2b9e7c5873cd2c72505b2d6) ### **Add Points/Redeem Mode** * If **Add Points** or **Redeem Points** features are enabled, the relevant tabs will appear in the bottom navigation bar. * This can be used for any pass that has **Enable loyalty balance** enabled on the pass template level. ![](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/passentry-reader-app/images/loyalty-balance.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=95574ec1fddc7a9cacc19a89497e2306) * To add or redeem points: 1. Enter the amount. 2. Click the action button at the bottom. 3. Prompt the user to scan their pass. 4. The points will be added or redeemed accordingly. ![](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/passentry-reader-app/images/add-points-one.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=5a5c53826f6ea1593d33abdb6c7c87ec) ![](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/passentry-reader-app/images/add-points-two.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=a6c0e077e9ab0d1d7f650e506ac21fb8) # Connecting Your POS to a PassEntry Terminal Source: https://docs.passentry.com/documentation/guides/pos-integration/guide Learn how to connect your point-of-sale system to a PassEntry terminal so customers can tap or scan their wallet pass for loyalty points, membership and event access, while you keep your existing payment hardware. A **PassEntry terminal** is a customer-facing device running the PassEntry Reader app in *Customer mode*. It can be a countertop payment terminal or card reader such as a PAX, Sunmi or Urovo device, a tablet, or a phone. It reads Apple Wallet and Google Wallet passes over NFC or QR, shows the customer what is happening, and hands the pass to **your** POS. Your POS decides what to do with it and calls the PassEntry API to validate the pass, add points or redeem points. Your POS pairs with a terminal by its ID, tells it when to read a pass and receives the pass back. Nothing about your checkout changes: your existing payment device still takes the payment. ## How It Works ```mermaid theme={null} flowchart LR POS["Your POS
(backend)"] Relay["PassEntry relay"] Term["PassEntry terminal
(customer-facing)"] API["PassEntry API"] POS <-- "WebSocket" --> Relay Relay <-- "WebSocket" --> Term POS -- "HTTPS" --> API style Term fill:#FF8477,stroke:#EC6759,color:#fff style API fill:#3B4851,stroke:#3B4851,color:#fff ``` **1. The terminal comes online** The terminal connects to the PassEntry relay and registers its **Terminal ID**, a 6-character code shown on the device. **2. Your POS logs in as a reader** Your POS signs in to the PassEntry API with a Reader ID and password, exactly as the PassEntry Reader app does, and keeps that session alive with a heartbeat. **3. Your POS pairs with the terminal** Your POS connects to the relay and pairs with the terminal by Terminal ID. **4. Your POS asks for a pass** When a customer is ready, your POS sends `start_capture`. The terminal shows a scan screen such as "Earn 50 points". **5. The customer taps or scans** The terminal reads the pass and sends `captured` with the pass identifier to your POS. **6. Your POS updates the pass and shows the result** Your POS calls the [Create scan](/api-reference/v1/scans/create-scan) endpoint to validate the pass, add points or redeem points, then sends `result`. The terminal shows a success or failure screen and returns to idle. Every API call the integration uses is listed in [The API calls behind the integration](#5-the-api-calls-behind-the-integration). The relay only forwards messages. It never reads passes, awards points or validates anything. Your POS owns the business logic. | Flow | Use it for | What the customer sees | | - | - | - | | **Scan** | Event access, membership check-in, stamp cards | A scan prompt, then a success or "invalid pass" screen | | **Accrue** | Adding loyalty points after a purchase | "Earn 50 points" over the scan prompt, then "Added Points 50" with your message | | **Redeem** | Spending loyalty points on a reward or discount | "Redeem 50 points" over the scan prompt, then "Points Redeemed 50" with your message | > [!TIP] > > Want to see it working before reading the detail? Jump to the [example](#example). It logs in, pairs with a terminal, asks for a QR scan and adds 10 points in under 80 lines of JavaScript. ## Before You Start * A **PassEntry Reader** for each till, created in the [Readers](https://app.passentry.com/readers) page of the Dashboard. Give it a name, a Reader ID such as `till-04`, a password, and enable the modes you plan to use (Scan, Redeem, Adding). Your POS logs in with these credentials. * A device to act as the terminal: a payment terminal or card reader with the PassEntry Reader app available in its app store, or any tablet or phone with NFC or a camera. * A backend service in your POS that can hold a WebSocket connection and make HTTPS calls. Connect from a server, not from a browser page. * **A pass identifier to test against**, for a pass with loyalty enabled on its template. Make a test pass in the [Dashboard](https://app.passentry.com) and note its UUID or external ID. Get it before you write any code: a reader session cannot look passes up or create one, so without an identifier you can sign in, pair a terminal and arm a capture, and still have nothing to complete a single API call with. If someone else runs the account, ask them for one. > [!NOTE] > > **A reader's configuration decides what your interface should offer.** The login response reports > it: `scanEnabled`, `addPointsEnabled` and `redeemEnabled` for what the reader may do, and > `qrEnabled` and `nfcEnabled` for how it can read a pass. Read all five at sign-in and show only > what applies. > > The first three are yours to enforce, one per operation: `scanEnabled` for a plain Scan, > `addPointsEnabled` for an Accrue, `redeemEnabled` for a Redeem. Reading a balance is not gated by > any of them. The API does not refuse a call because a mode is switched off, so a reader with > `addPointsEnabled: false` can still accrue and get an ordinary `200`. The two read flags are > enforced by the terminal. > > A reader is signed in from one place at a time, and each login invalidates the previous session, so > give the POS its own reader rather than sharing one with a physical PassEntry Reader device. ## Quickstart Everything below is the full guide. If you would rather see it working first, these four commands are the whole integration with nothing left out. They need a Reader ID, its password, and a pass identifier (see [Before You Start](#before-you-start)). No terminal is involved. Every field shown is required. Three of them are easy to miss: the body must be wrapped in `passentry_reader`, and a scan needs both `readerId` and `scanType`. ```bash 1. Sign in theme={null} curl -X POST https://api.passentry.com/api/v1/readers/login \ -H "Content-Type: application/json" \ -d '{ "passentry_reader": { "uuid": "YOUR_READER_ID", "password": "YOUR_PASSWORD" } }' # → data.attributes.token use as the Bearer token below # → data.attributes.scanEnabled / addPointsEnabled / redeemEnabled # what this reader is set up for: show only those actions ``` ```bash 2. Look up a pass theme={null} curl https://api.passentry.com/api/v1/loyalty/YOUR_PASS_ID \ -H "Authorization: Bearer YOUR_TOKEN" # → meta.loyalty.balance the number to show the customer ``` ```bash 3. Add points theme={null} curl -X POST https://api.passentry.com/api/v1/scans \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "scan": { "readerId": "YOUR_READER_ID", "passId": "YOUR_PASS_ID", "scanType": "barcode", "loyalty": { "balance": 5, "message": "You earned 5 points." } } }' # → validationResponse.status "successful" or "failed" # → validationResponse.readerInstructions.message show this to the customer ``` ```bash 4. Redeem points theme={null} curl -X POST https://api.passentry.com/api/v1/scans \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "scan": { "readerId": "YOUR_READER_ID", "passId": "YOUR_PASS_ID", "scanType": "barcode", "loyalty": { "balance": -5, "message": "You redeemed 5 points." } } }' # a negative balance redeems; too few points returns 422 with the current balance ``` Keep the session alive with a heartbeat every 5 minutes, or the token expires after 30 ([Send a heartbeat](#send-a-heartbeat-every-5-minutes)). Read the pass identifier off a customer's phone with a PassEntry terminal by adding the relay, which is what the rest of this guide covers. ## 1. Set Up the Terminal **1. Install the app** Install the **PassEntry NFC/QR Pass Scanner** app from the Apple App Store, Google Play Store, PAX MAXSTORE, Sunmi App Store or Urovo App Store. **2. Switch to Customer mode** Open the app. On the login screen, tap **Mode: Standalone ▾** at the bottom and choose **Customer**. Do not log in. The app switches to a customer-facing idle screen showing the PassEntry logo, and remembers this choice across restarts and updates. **3. Note the Terminal ID** Tap the grey **gear** icon in the top-right corner of the idle screen. The menu shows **Terminal ID: XXXXXX**, for example `2NJE4Y`. It stays the same until the app is uninstalled, so write it on the device or store it against the till in your POS. **4. Leave it connected** The terminal connects to `wss://pos.passentry.com/cable` as soon as it enters Customer mode, keeps that connection open and reconnects on its own after a network drop. It needs outbound access to `pos.passentry.com` on port 443, a stable Wi-Fi or Ethernet connection and mains power. ![The login screen with the Mode menu open, showing Standalone, Merchant and Customer](https://mintcdn.com/passentry-0/pFlwF7RgdN_1cBGG/documentation/guides/pos-integration/images/mode-menu.png?fit=max\&auto=format\&n=pFlwF7RgdN_1cBGG\&q=85\&s=dcbb052d9926029feff2446c2b53d89e) ![The Customer mode idle screen with the gear menu open, showing the Terminal ID](https://mintcdn.com/passentry-0/pFlwF7RgdN_1cBGG/documentation/guides/pos-integration/images/terminal-id.png?fit=max\&auto=format\&n=pFlwF7RgdN_1cBGG\&q=85\&s=186a94032a7cbb681c520cbe3916dc15) > [!WARNING] > > Type the Terminal ID exactly as shown, in uppercase. Pairing is case-sensitive. The ID never contains `0`, `O`, `1` or `I`. To leave Customer mode, open the gear menu and choose **Log out**. ## 2. Log In as the Reader Your POS authenticates the same way the PassEntry Reader app does: it signs in with the reader's ID and password, receives a short-lived token, and sends a heartbeat every 5 minutes to keep the session alive and rotate the token. The four calls are [Reader login](/api-reference/v1/readers/reader-login), [Reader heartbeat](/api-reference/v1/readers/reader-heartbeat), [Refresh reader token](/api-reference/v1/readers/refresh-reader-token) and [Reader logout](/api-reference/v1/readers/reader-logout). ### Sign in > [!WARNING] > > The body must be wrapped in **`passentry_reader`**. Any other wrapper returns > `404 Invalid credentials`, exactly as if the Reader ID did not exist. ```bash theme={null} curl -X POST https://api.passentry.com/api/v1/readers/login \ -H "Content-Type: application/json" \ -d '{ "passentry_reader": { "uuid": "till-04", "password": "{PASSWORD}" } }' ``` ```json theme={null} { "data": { "id": "till-04", "type": "reader", "attributes": { "name": "Till 4", "readerType": "PassentryReader", "loggedIn": true, "qrEnabled": true, "mifareClassicNfcEnabled": false, "ndefTagScanningEnabled": false, "scanEnabled": true, "redeemEnabled": true, "addPointsEnabled": true, "lastSeenAt": "2026-09-07T19:11:28Z", "readerConfiguration": { "nfcEnabled": true, "nfcPrivateKey": "-----BEGIN EC PRIVATE KEY-----\n…\n-----END EC PRIVATE KEY-----", "googleCollectorID": "95415519", "applePassTypeID": "pass.com.example.loyalty", "googleKeyVersion": 1 }, "validationConfiguration": { "validationType": "internal" }, "token": "eyJhbGciOiJIUzI1NiJ9…", "refreshToken": "eyJhbGciOiJIUzI1NiJ9…" } } } ``` Keep three things from the response: * `token`: the bearer token for every API call and for the relay connection. It expires after **30 minutes**. * `refreshToken`: lets you obtain a new token without the password. It expires after 6 weeks. * `readerConfiguration`: the NFC credentials you will send to the terminal (see [Reading passes over NFC](#reading-passes-over-nfc)). A wrong password returns `401` and an unknown Reader ID returns `404`, both with `{"errors": ["Invalid credentials"]}`. A body wrapped in the wrong key returns the same `404` as an unknown Reader ID, so if you get a `404` for a reader you know exists, check that your JSON is wrapped in `passentry_reader` before you look anywhere else. ### Send a heartbeat every 5 minutes ```bash theme={null} curl https://api.passentry.com/api/v1/readers/till-04/heartbeat \ -H "Authorization: Bearer {TOKEN}" ``` ```json theme={null} { "message": "Heartbeat successful", "token": "eyJ…", "refresh_token": "eyJ…" } ``` Each heartbeat marks the reader as online in the Dashboard and returns a **new** token and refresh token. Switch to the new token straight away: the previous one stops working the moment the heartbeat succeeds. Run the heartbeat on a 5 minute timer for as long as the POS is open, which keeps you well inside the 30 minute expiry. ### Recover from a 401 If an API call returns `401`, the token has expired or the reader was signed in elsewhere. Exchange the refresh token for a new pair, or sign in again with the password if the refresh token is also rejected: ```bash theme={null} curl -X POST https://api.passentry.com/api/v1/readers/refresh_token \ -H "Content-Type: application/json" \ -d '{ "refresh_token": "{REFRESH_TOKEN}" }' ``` ```json theme={null} { "message": "token refreshed", "token": "eyJ…", "refresh_token": "eyJ…" } ``` ### Sign out When the till closes, release the reader session: ```bash theme={null} curl -X DELETE https://api.passentry.com/api/v1/readers/till-04/logout \ -H "Authorization: Bearer {TOKEN}" ``` ## 3. Connect and Pair The relay speaks the [Action Cable](https://guides.rubyonrails.org/action_cable_overview.html) protocol over a WebSocket. Every frame is JSON, and the `identifier` and `data` fields are themselves JSON encoded as strings. ### Open the socket ``` wss://pos.passentry.com/cable?poiid={READER_ID}&token={TOKEN} ``` **poiid** `string` *required* Your Reader ID, for example `till-04`. It identifies this POS connection and is recorded as the owner of any terminal you pair with. **token** `string` *required* Your current reader token from login or the last heartbeat. Send the sub-protocol header `Sec-WebSocket-Protocol: actioncable-v1-json`. The relay replies with `{"type":"welcome"}` once the socket is open. The socket stays valid while it is open; use whichever token is current when you reconnect. > [!WARNING] > > Connect from your backend. The relay accepts connections with no `Origin` header (server-side WebSocket clients) or an `Origin` of `https://pos.passentry.com`. A browser page on any other origin is rejected with HTTP 404 during the handshake. ### Pair with the terminal Subscribe to `RelayChannel` with the Terminal ID: ```json theme={null} { "command": "subscribe", "identifier": "{\"channel\":\"RelayChannel\",\"terminal\":\"2NJE4Y\"}" } ``` The relay answers with `{"type":"confirm_subscription", ...}` when the terminal is online and you now own it, or `{"type":"reject_subscription", ...}` when the Terminal ID is blank or the terminal is not connected. A rejection has no error body. If the terminal has just been switched on, wait a few seconds and retry. Only one POS should drive a terminal at a time. If a second POS subscribes to the same Terminal ID, the relay hands ownership to the newest subscriber and the earlier one stops receiving the terminal's messages. ### Keep the pairing alive Ownership expires after 60 seconds without activity. Call the `ping` action every 20 to 30 seconds while paired. This is separate from the 5 minute API heartbeat above: the API heartbeat keeps your reader session alive, the relay ping keeps your claim on the terminal alive. The reply arrives wrapped in your subscription, like every other message from the relay, so read it from `message.type` rather than the top level: ```json theme={null} { "identifier": "{\"channel\":\"RelayChannel\",\"terminal\":\"2NJE4Y\"}", "message": { "type": "pong" } } ``` ```json theme={null} { "command": "message", "identifier": "{\"channel\":\"RelayChannel\",\"terminal\":\"2NJE4Y\"}", "data": "{\"action\":\"ping\"}" } ``` The relay also sends `{"type":"ping","message":}` frames every few seconds. Those need no reply. ### Send and receive Send a message to the terminal with the `to_reader` action. Everything in `data` except `action` is forwarded unchanged: ```json theme={null} { "command": "message", "identifier": "{\"channel\":\"RelayChannel\",\"terminal\":\"2NJE4Y\"}", "data": "{\"action\":\"to_reader\",\"type\":\"start_capture\",\"flow\":\"SCAN\",\"mode\":\"QR\"}" } ``` Messages from the terminal arrive wrapped in your subscription identifier. Dispatch on `message.type` and ignore any type you do not recognise: ```json theme={null} { "identifier": "{\"channel\":\"RelayChannel\",\"terminal\":\"2NJE4Y\"}", "message": { "type": "captured", "passData": "7882e98b-0eba-4625-9223-5221eb8dad7f" } } ``` Closing the socket or sending an `unsubscribe` command releases the terminal. If the socket drops mid-transaction, reconnect, wait for `welcome`, re-subscribe, send `cancel` and start the capture again. ## 4. Run a Scan, Accrue or Redeem All three flows follow the same four steps. Only the `flow` value and the API call you make differ. ```mermaid theme={null} sequenceDiagram autonumber participant POS as Your POS participant T as PassEntry terminal participant API as PassEntry API POS->>T: start_capture (flow, mode, amountMinor) Note over T: Shows "Earn 50 points"
Customer taps or scans T->>POS: captured (passData, mode) Note over T: Shows "Processing"
waits up to 15 s POS->>API: POST /api/v1/scans API-->>POS: validationResponse POS->>T: result (success, message) Note over T: Shows the result for 3 s,
then returns to idle ``` **1. Start the capture** ```json theme={null} { "action": "to_reader", "type": "start_capture", "flow": "ACCRUE", "mode": "QR", "amountMinor": 50 } ``` With no `readerConfig`, the terminal opens the camera and scans a QR code. To read passes over NFC, include `readerConfig` with the credentials from your login response (see [Reading passes over NFC](#reading-passes-over-nfc)). NFC needs both: the credentials from you, and `nfcEnabled` on the reader itself. With both in place the terminal offers the camera and NFC at once for these flows, so send `readerConfig` whenever the reader supports NFC and let the customer choose. For Accrue and Redeem, `amountMinor` is the number of points shown to the customer: "Earn 50 points" or "Redeem 50 points" above the scan screen, and again on the success screen. It is display only. The API call decides the real amount. ![The terminal scan screen with an Earn 50 points banner above the camera view](https://mintcdn.com/passentry-0/pFlwF7RgdN_1cBGG/documentation/guides/pos-integration/images/capture-qr.png?fit=max\&auto=format\&n=pFlwF7RgdN_1cBGG\&q=85\&s=7681bf9e7aab735d047ca73ef53c2d93) **2. Receive the pass** The terminal sends one `captured` per `start_capture`: ```json theme={null} { "type": "captured", "passData": "7882e98b-0eba-4625-9223-5221eb8dad7f", "mode": "NFC" } ``` `passData` is the value read from the pass. Over NFC it is the pass's NFC value, which defaults to the pass UUID. Over QR it is the barcode content, which also defaults to the pass UUID. If your pass template uses a custom NFC value or barcode, map it to a pass in your own system first, or use [third-party validation](../validation-options/guide) with the [Log scan](/api-reference/v1/scans/log-scan) endpoint. `mode` is how the terminal read the pass: `NFC` or `QR`. Older terminal builds omit it, so treat a missing `mode` as `QR`. Use it to set `scanType` in the next step. This is the method the customer actually used, which may not be the one you asked for: when the reader's configuration allows both, the terminal offers the camera and NFC together. **3. Call the PassEntry API** Use `passData` as `passId` in a [Create scan](/api-reference/v1/scans/create-scan) request, authenticated with your current reader token. `readerId` is your Reader ID. Set `scanType` from the capture `mode`: `nfc` when it was `NFC`, otherwise `barcode`. `passId` accepts either the pass UUID or the pass's external ID, so if your passes were issued with your own customer number as the external ID you can pass that directly. The response carries both identifiers as `scanInfo.passId` and `scanInfo.passExtId`, which is the easiest way to link the scan back to a customer record in your POS. One call covers all three. A positive `balance` accrues, a negative one redeems, and no `loyalty` object at all is a plain Scan (which adds a stamp to a stamp-card pass). `message` is the wallet notification the customer receives. Send whole numbers. This integration deals in whole points. A template can be configured for decimal balances instead, in which case the balance comes back as a float; if that is your setup, read [Loyalty](/api-reference/v1/loyalty/guide) first. ```bash theme={null} curl -X POST https://api.passentry.com/api/v1/scans \ -H "Authorization: Bearer {TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "scan": { "readerId": "till-04", "passId": "7882e98b-0eba-4625-9223-5221eb8dad7f", "scanType": "barcode", "loyalty": { "balance": 50, "message": "You earned 50 points." } } }' ``` The response always has the same shape: a `validationResponse` with the outcome and the message to show, and a `scanInfo` record of the scan. `loyalty` is present only when points changed, and `passExtId` is null unless the pass was issued with an external ID. ```json theme={null} { "validationResponse": { "status": "successful", "statusDetail": null, "readerInstructions": { "message": "50 points added to your balance. Current balance: 275" }, "loyalty": { "balanceChange": "50", "updatedBalance": "275", "operation": "add" } }, "scanInfo": { "scanId": "019488b8-c2fc-7b5b-b5b4-deb2b7f33bdd", "scanType": "barcode", "scanValue": "7882e98b-0eba-4625-9223-5221eb8dad7f", "validationType": "internalValidation", "status": "successful", "statusDetail": null, "scannedAt": "2026-09-15T11:56:37Z", "passId": "7882e98b-0eba-4625-9223-5221eb8dad7f", "passExtId": "CUST-000123", "readerId": "till-04" } } ``` A redeem reads "You successfully redeemed 50 points. Current balance: 225" with `operation` `redeem`; a plain Scan reads "Scan was validated successfully" with no `loyalty` object, or "Your card was stamped successfully" with `operation` `stamp` on a stamp card. A pass that fails validation still returns **200**, with `status: "failed"` and the reason in `statusDetail` and `readerInstructions.message`. The scan is recorded and no points change: ```json theme={null} { "validationResponse": { "status": "failed", "statusDetail": "Pass is not active", "readerInstructions": { "message": "Pass is not active" } }, "scanInfo": { "scanId": "…", "status": "failed", "statusDetail": "Pass is not active", "passId": "…", "passExtId": "…", "readerId": "till-04" } } ``` The two validation failures are `Pass is not active` (the pass is expired, deactivated or nullified) and `Pass already used within validity period` (a repeat scan inside the validity window set on your reader configuration). > [!WARNING] > > **Repeat posts are not deduplicated.** There is no idempotency key on `POST /api/v1/scans`, and > a reader session cannot look a scan up afterwards, so a retry after a timeout will move the > balance twice. If a call times out you do not know whether it landed: read the balance back with > [Get loyalty balance](/api-reference/v1/loyalty/show-loyalty-balance) before retrying, and guard > the button against a double press at the till. Requests that cannot be processed at all return an error status with an `errors` array. Every response carries rate limit headers — `x-rate-limit-limit`, `x-rate-limit-remaining` and `x-rate-limit-reset` (a Unix timestamp), scoped per endpoint by `x-rate-limit-url`. The allowance is 10 per endpoint and refills continuously, so a till serving customers will not approach it. A burst can: fired back to back from one process, reads exhaust it and the API starts returning `429`. Watch `x-rate-limit-remaining` and back off on a `429` rather than retrying immediately, particularly in anything that loops. | Status | Body | When | | - | - | - | | `404` | `{"errors": ["Pass does not exist"]}` | `passId` matches no live pass in your entity. `GET /loyalty/{id}` words the same condition differently, `{"errors": ["Pass with provided id not found"]}`, so match on the status rather than the text | | `404` | `{"errors": ["Reader does not exist"]}` | `readerId` is not the reader you logged in as | | `422` | `{"errors": ["Insufficient balance for the requested deduction, current balance is 20"]}` | Redeem for more points than the pass holds | | `422` | `{"errors": ["Balance must be a number"]}` | `loyalty.balance` is not numeric. Send a number, not a numeric string | | `422` | `{"errors": ["Scan type can't be blank"]}` | `scanType` was omitted. It is required on every scan, including a plain Scan with no `loyalty` object | | `422` | `{"errors": ["'magstripe' is not a valid scan_type"]}` | `scanType` must be exactly `nfc` or `barcode` | | `412` | `{"errors": ["Loyalty is not enabled for this pass. Please enable it at the pass template level"]}` | Accrue or Redeem on a template without loyalty balance | | `401` | `{"errors": ["Access denied. Please, sign in again."]}` | Reader token expired or rotated | | `401` | `{"errors": ["Access denied"]}` | The `Authorization` header is missing or is not a token at all. Match on the status, not the wording: the text differs between a spent token and an unreadable one | | `403` | `{"errors": ["Reader is not authorized to perform this action."]}` | The reader is not enabled for this operation, or the endpoint is not available to a reader session at all | **4. Send the result** Send `result` within **15 seconds** of receiving `captured`. The terminal shows the outcome for 3 seconds and returns to idle. ```json theme={null} { "action": "to_reader", "type": "result", "success": true } ``` ```json theme={null} { "action": "to_reader", "type": "result", "success": false, "message": "Not enough points" } ``` On success the terminal shows the flow's success screen with your `message` as the heading, or a default of "Pass Valid", "Points Added" or "Points Redeemed" when you omit it. Accrue and Redeem also show the points amount from `start_capture`. On failure it shows an "invalid pass" screen with your `message`. > [!TIP] > > Pass `validationResponse.readerInstructions.message` from the API response straight through as `message`. The customer then sees the same wording as your POS, for example "50 points added to your balance. Current balance: 275". ![Green success screen reading Added Points 50 with the message 50 points added to your balance](https://mintcdn.com/passentry-0/pFlwF7RgdN_1cBGG/documentation/guides/pos-integration/images/accrue-success.png?fit=max\&auto=format\&n=pFlwF7RgdN_1cBGG\&q=85\&s=c68f8835111a026e7176da5a6fcb4aa4) ![Red failure screen reading Not enough points](https://mintcdn.com/passentry-0/pFlwF7RgdN_1cBGG/documentation/guides/pos-integration/images/redeem-failed.png?fit=max\&auto=format\&n=pFlwF7RgdN_1cBGG\&q=85\&s=dfcb23e5dfc87909bae6f3106f7a2225) > [!WARNING] > > If no `result` arrives within 15 seconds, the terminal returns to idle without telling the customer whether anything happened. If the API call is slow or errors, send a failure result at your own deadline and reconcile afterwards. ### Cancel a capture ```json theme={null} { "action": "to_reader", "type": "cancel" } ``` Send `cancel` when staff back out of the transaction, and **when your own timeout expires**. The terminal's scan screen has no timeout of its own, so if your POS stops waiting after 60 seconds and does not say so, the terminal stays armed and a later tap goes nowhere. Sending a new `start_capture` while one is in progress replaces it. A timeout tells you nothing about why. The customer may not have presented anything, another POS may have taken the terminal, or your `start_capture` may never have arrived, since `to_reader` is not acknowledged. All three look identical: no frame, no error, no socket close. Treat a timeout as "unknown", ask the cashier to try again, and do not report to them that the customer walked away. ## 5. The API Calls Behind the Integration The reader token from step 2 authenticates every call. Create scan is covered in step 4; the two balance calls and end-of-day reconciliation are below. ### Check a balance before redeeming Use the `passData` from `captured` as the pass ID. The balance is returned in `meta.loyalty.balance`, as an integer or a decimal depending on the pass template. ```bash theme={null} curl https://api.passentry.com/api/v1/loyalty/7882e98b-0eba-4625-9223-5221eb8dad7f \ -H "Authorization: Bearer {TOKEN}" ``` ```json theme={null} { "data": { "id": "7882e98b-0eba-4625-9223-5221eb8dad7f", "type": "pass", "attributes": { "status": "active", "extId": "CUST-000123", "passTemplateName": "PassEntry Coffee" } }, "meta": { "loyalty": { "balance": 225 } } } ``` A typical redeem flow is: capture the pass, read the balance, decide the discount, then send the Create scan request with the negative `balance` and finish with `result`. Keep all three inside the 15 second window, or send `result` as soon as the scan call returns and read the balance beforehand. ### Adjust a balance without a tap Update loyalty balance changes a pass's balance directly and sends the customer a wallet notification. Use it when there is no customer at the terminal, for example refunding points after a returned item or correcting a mistake. It does not record a scan or apply the reader's validity period, so keep Create scan for anything that happens at the till. ```bash theme={null} curl -X PATCH https://api.passentry.com/api/v1/loyalty/7882e98b-0eba-4625-9223-5221eb8dad7f \ -H "Authorization: Bearer {TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "balance": -20, "message": "20 points removed for your refunded order." }' ``` `balance` adds when positive and subtracts when negative. Send `overrideBalance` instead to set an absolute value. The response has the same shape as Get loyalty balance, with the new balance in `meta.loyalty.balance`. A non-numeric balance returns `400` with `{"errors": ["Balance must be a number"]}`. ### Reconciliation and reporting Scan history is not available to a reader session. [List scans](/api-reference/v1/scans/get-scans) and [Get scan](/api-reference/v1/scans/get-scan) return `403 Reader is not authorized to perform this action` for a reader token. For end-of-day reconciliation, call them from your backend with an organisation [API key](https://app.passentry.com/developer-tools) and filter by `readerId`, `passId` or `createdAfter`. Every scan your POS created in step 4 appears there with its `scanId`, so store that ID against your transaction when the Create scan response arrives. ## Reading Passes Over NFC Reading a wallet pass over NFC requires credentials that identify your entity to Apple Wallet and Google Wallet and decrypt the value the pass returns. Because a terminal in Customer mode has no account of its own, your POS sends these credentials with each `start_capture`. They are already in the `readerConfiguration` object of your login response. Map them into `readerConfig` like this: | Login response | `readerConfig` field | | - | - | | `nfcPrivateKey` | `privateKey` | | `googleCollectorID` | `googleCollectorId` | | `applePassTypeID` | `applePassTypeId` | | `googleKeyVersion` | `googleKeyVersion` | | `nfcEnabled` | `nfcEnabled` | ```json theme={null} { "action": "to_reader", "type": "start_capture", "flow": "REDEEM", "mode": "NFC", "amountMinor": 50, "readerConfig": { "nfcEnabled": true, "qrEnabled": true, "privateKey": "-----BEGIN EC PRIVATE KEY-----\n…\n-----END EC PRIVATE KEY-----", "googleCollectorId": "95415519", "applePassTypeId": "pass.com.example.loyalty", "googleKeyVersion": 1 } } ``` With both `nfcEnabled` and `qrEnabled` set, the terminal shows a toggle between NFC and camera, starting on the method named in `mode`. A device without NFC hardware uses the camera when `qrEnabled` is `true`. > [!WARNING] > > If you include `readerConfig` at all, it must contain `privateKey`, `googleCollectorId` and `applePassTypeId`. A `readerConfig` missing any of them makes the whole `start_capture` unreadable and the terminal ignores it. For QR only, leave `readerConfig` out. > > The private key lets a device decrypt your customers' pass values. Only send it over the TLS WebSocket, only to terminals you operate, and never log it. ## Message Reference Messages are JSON objects with a `type` field. Unknown fields are ignored on both sides. ### POS to terminal **start\_capture** `object` Arms the terminal to read one pass. **Properties** **flow** `string` *required* `SCAN`, `ACCRUE` or `REDEEM`. Controls the banner during capture and the wording of the success screen. **mode** `string` *required* `NFC` or `QR`. Which capture method to show first. Cannot enable a method that `readerConfig` has disabled. **amountMinor** `integer` For `ACCRUE` and `REDEEM`, the number of points to display during the capture and on the success screen. Omit for `SCAN`. **readerConfig** `object` Omit for QR only. When present, `privateKey`, `googleCollectorId` and `applePassTypeId` are required; `googleKeyVersion` defaults to `1`, `nfcEnabled` to `false` and `qrEnabled` to `true`. **result** `object` The outcome to show the customer. Must arrive within 15 seconds of `captured`. **Properties** **success** `boolean` *required* `true` shows the flow's success screen, `false` the invalid-pass screen. **message** `string` Heading shown on the success or failure screen. Omit on success to get the default heading for the flow. Keep it short, or pass the API's `readerInstructions.message` through unchanged. **cancel** `object` Aborts the current capture and returns the terminal to idle. Ignored when the terminal is already idle. ### Terminal to POS **captured** `object` The customer presented a pass. Sent once per `start_capture`. **Properties** **passData** `string` *required* The value read from the pass. By default the pass UUID. **mode** `string` `NFC` or `QR`, how the terminal read the pass. Absent on older terminal builds, which you should treat as `QR`. Map `NFC` to `scanType: "nfc"` and anything else to `scanType: "barcode"`. Other types may appear in future. Ignore any you do not recognise. ## Example A minimal Node.js client that logs in, heartbeats, pairs, asks for a QR scan and adds 10 points. It needs Node 18 or later and the `ws` package. ```javascript theme={null} import WebSocket from "ws"; const API = "https://api.passentry.com/api/v1"; const RELAY = "wss://pos.passentry.com/cable"; const READER_ID = process.env.READER_ID; // e.g. till-04 const PASSWORD = process.env.READER_PASSWORD; const TERMINAL = process.env.TERMINAL_ID; // shown in the terminal's gear menu, e.g. 2NJE4Y const POINTS = 10; // Sign in as the reader. The token expires after 30 minutes; the heartbeat below rotates it. const login = await fetch(`${API}/readers/login`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ passentry_reader: { uuid: READER_ID, password: PASSWORD } }), }).then((r) => r.json()); let token = login.data.attributes.token; // What this reader is configured to do. The API will not refuse a disabled mode, so this is the // only thing stopping a cashier from running an operation the reader was not set up for. const { scanEnabled, addPointsEnabled, redeemEnabled } = login.data.attributes; const allowed = { SCAN: scanEnabled, ACCRUE: addPointsEnabled, REDEEM: redeemEnabled }; function assertAllowed(flow) { if (!allowed[flow]) throw new Error(`This reader is not set up for ${flow}`); } // Heartbeat every 5 minutes, exactly like the PassEntry Reader app. Each reply carries a new token. setInterval(async () => { const hb = await fetch(`${API}/readers/${READER_ID}/heartbeat`, { headers: { Authorization: `Bearer ${token}` }, }).then((r) => r.json()); if (hb.token) token = hb.token; }, 5 * 60 * 1000); const identifier = JSON.stringify({ channel: "RelayChannel", terminal: TERMINAL }); const ws = new WebSocket(`${RELAY}?poiid=${READER_ID}&token=${token}`, "actioncable-v1-json"); const send = (command, data) => ws.send(JSON.stringify({ command, identifier, ...(data && { data: JSON.stringify(data) }) })); const toReader = (message) => send("message", { action: "to_reader", ...message }); ws.on("message", async (raw) => { const frame = JSON.parse(raw); if (frame.type === "welcome") return send("subscribe"); if (frame.type === "reject_subscription") return ws.close(1000, "terminal offline"); if (frame.type === "confirm_subscription") { setInterval(() => send("message", { action: "ping" }), 25_000); assertAllowed("ACCRUE"); // check before you arm the terminal, not after the customer has tapped return toReader({ type: "start_capture", flow: "ACCRUE", mode: "QR", amountMinor: POINTS }); } if (frame.message?.type !== "captured") return; const res = await fetch(`${API}/scans`, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" }, body: JSON.stringify({ scan: { readerId: READER_ID, passId: frame.message.passData, scanType: frame.message.mode === "NFC" ? "nfc" : "barcode", loyalty: { balance: POINTS, message: `You earned ${POINTS} points.` }, }, }), }); const body = await res.json(); const ok = res.ok && body.validationResponse?.status === "successful"; toReader({ type: "result", success: ok, message: ok ? body.validationResponse.readerInstructions?.message : body.errors?.[0] ?? "Could not add points", }); console.log(ok ? `balance is now ${body.validationResponse.loyalty.updatedBalance}` : body); setTimeout(() => ws.close(), 500); }); ``` What you should see: 1. The reader shows as logged in on the [Readers](https://app.passentry.com/readers) page. 2. The terminal leaves the logo screen and shows **"Earn 10 points"** above a camera view. 3. Hold the pass's QR code up to the camera. The terminal shows **Processing**. 4. The script prints `balance is now …`. 5. The terminal shows **Added Points 10** with the API's message, "10 points added to your balance. Current balance: …", for three seconds, then returns to the logo. 6. The customer's phone receives a wallet notification: "You earned 10 points." ## Troubleshooting > **Login returns 401 or 404 Invalid credentials** > > The Reader ID or password is wrong, or the reader is a VTAP reader rather than a PassEntry Reader. Check the [Readers](https://app.passentry.com/readers) page. > **API calls return 401 Access denied. Please, sign in again.** > > The token expired (30 minutes without a heartbeat), you kept using the old token after a heartbeat rotated it, or the same reader was signed in somewhere else. Use the token from the latest heartbeat, exchange the refresh token, or sign in again. Give each POS its own reader. > **Handshake fails with HTTP 404** > > You connected from a browser page on another origin. Connect from your backend. > **Subscription is rejected** > > Wrong or lowercase Terminal ID, or the terminal is offline. Check the gear menu, confirm the device shows the idle logo screen, wait 10 seconds and retry. > **Paired, but the terminal ignores start\_capture** > > One of three things: another POS paired with the same Terminal ID since, `readerConfig` is missing a credential field, or `flow` or `mode` is not one of the uppercase values above. > **Terminal returned to idle before I sent result** > > The 15 second window passed. Reply faster, or send a failure result at your own deadline. > **Terminal stays on the scan screen** > > Your POS stopped waiting without sending `cancel`. > **API returns 404 Reader does not exist** > > `readerId` must be the Reader ID you logged in with. A reader token cannot record scans against another reader. > **API returns 404 Pass does not exist** > > `passId` must be a pass UUID or external ID. Custom NFC or barcode values need mapping first. > **API returns 422 on a redeem** > > Not enough points on the pass. Send a failure result with a clear message. > **API returns 412** > > Loyalty balance is not enabled on the pass template. If you are still stuck, email [support@passentry.com](mailto:support@passentry.com) with the Reader ID, the Terminal ID, the time of the transaction and the frames your POS sent and received. # Using Third-Party Validation with PassEntry Source: https://docs.passentry.com/documentation/guides/validation-options/guide Learn how to configure third-party validation so your own system can control how passes are validated during scans, including custom validation logic, reader behaviour and optional loyalty updates in a single request. PassEntry supports validating passes at physical locations using NFC or barcodes, for use cases such as event access, membership verification, ticketing and loyalty check-ins. By default, PassEntry validates scans internally. With **third-party validation**, each scan is sent to your own system, allowing you to apply custom business logic, control reader responses and optionally update loyalty balances as part of the same validation flow. ## What is Third-Party Validation? With **third-party validation**, your system takes full control over how passes are validated. Instead of PassEntry checking pass validity, every scan is sent to your own endpoint, where you decide the logic. ### PassEntry Internal vs. Third-Party Validation | Feature | PassEntry Default Validation 📦 | Third-Party Validation 🔧 | | - | - | - | | **Setup** | 📦 Out-of-the-box, no setup required | 🛠️ Requires a custom validation endpoint | | **Validation Logic** | 🔒 PassEntry validates against its own database | ⚙️ Your system defines all validation rules | | **Scan Value Format** | 🔑 Must be pass UUID or external ID | 📜 Can contain any data or combination of values (e.g., `customerID-promoCode`) | | **Reader Instructions** | 📡 Pre-defined reader instructions | 🎨 Full control over instructions (e.g., messages, LED colors, sounds) | | **Scans Logging** | 📊 Yes | 📊 Yes | | **Support for NFC/Barcode** | 📡 NFC & Barcode (only if barcode is configured as pass UUID) | 📡 NFC & Barcode | | **Association to the pass** | 🔄 Yes | 📡 Only if `passId` provided by your endpoint | | **Current Compatibility** | 🔗 Works with VTAP & PassEntry Reader | 🔗 Works with VTAP & PassEntry Reader | | **Loyalty Management** | ✨ Automatically updates points or stamps, and sends notifications if included | 🎯 Full control – your endpoint must handle loyalty updates and send any notifications | ## When to Use Third-Party Validation Use third-party validation when you need full control over validation logic, such as enforcing custom access rules, validating external identifier or updating loyalty balances based on business-specific conditions. If you do not need custom logic, PassEntry’s default validation is recommended as it requires no setup and works out of the box. ## How to Enable Third-Party Validation To start using third-party validation, follow these simple steps: ### 1. Ensure You Have a Reader Set Up Third-party validation is currently supported **only on PassEntry readers**. If you haven’t set up a reader yet, [follow this guide](../passentry-reader-app/guide) to do so. ### 2. Update Your Reader Configuration 1. **Go to** **PassEntry Dashboard** → **Readers** → [**Reader Configuration**](https://app.passentry.com/readers/reader-configuration). 2. Find the **Validation Settings** section. 3. Change the validation type to **Third-Party**. 4. Enter an HTTPS URL of your **third-party validation endpoint** (this is where PassEntry will send scans for validation). 5. Include a **Bearer token** (this is a token that the reader will send to your endpoint to authenticate itself). ![Validation Settings](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/validation-options/images/validation-settings.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=b78ecfc649b9f98bec5b5fa449b3a7aa) ### 3. Apply Changes & Activate Third-Party Validation * Once you update the settings, the PassEntry reader will **log out** automatically and VTAP reader's configuration will be automatically updated. * On the next login/reboot, it will **use the new validation settings**, sending all scans to your third-party endpoint. * After validation, **scans will still be logged in PassEntry** for tracking and reporting (only on PassEntry readers). ![Third-Party Logs](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/validation-options/images/third-party-logs.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=cc9010c009ce39eb8a27026d813fbeea) ## How It Works 1. A pass is scanned using a **PassEntry reader** or **VTAP reader** (via NFC or barcode). 2. Instead of PassEntry validating the pass, the scan is **sent to your third-party endpoint**. 3. Your system **processes the validation** using custom business logic. 4. The reader receives a **response** from your system and displays any necessary instructions (e.g., “Access Granted” or “Invalid Ticket”). 5. The scan is logged in PassEntry for visibility and reporting (only on PassEntry readers). ## Third-Party Endpoint Structure Your custom validation endpoint must be able to **receive scan data** and **respond with validation results**. ### Reader Request Format #### PassEntry Reader ```json theme={null} { "scan": { "scanValue": "some-scan-value", "readerId": "passentry_reader_id", "scanType": "nfc", "loyalty": { "balance": -10, "message": "You have redeemed 10 points" } } } ``` **scan** `object` **Properties** **scanValue** `string` The value read from the NFC tag or barcode **readerId** `string` Unique identifier of the PassEntry reader **scanType** `string` Type of scan performed. Can be either `"nfc"` or `"barcode"` **loyalty** `object` Optional. Object containing loyalty information. **Properties** **balance** `number` Loyalty points to add or subtract from the user's balance. Can't be used together with `overrideBalance`. **overrideBalance** `number` Optional. Override the balance of the loyalty points. Can't be used together with `balance`. **message** `string` Optional. A notification message to send to the user. #### VTAP Reader ```json theme={null} { "scan": { "scanValue": "some-scan-value", "readerId": "CC123456", "readerType": "VTAP", "scanData": { "IP": "Local IP of device", "location": "Reader Location", "passtype": "A", "payload": "d9ae223c9e6cd57e18f3f61b6198cce756de8b2b", "rdrparam1": "Optional Reader Info 1", "rdrparam2": "Optional Reader Info 2", "serialno": "CC123459", "SSID": "Connected WiFi" } } } ``` **scan** `object` **Properties** **scanValue** `string` The value read from the NFC tag or barcode **readerId** `string` Unique identifier of the VTAP reader **readerType** `string` Type of reader **scanData** `object` Additional data about the scan from the VTAP reader. ### Your Endpoint Response Format #### PassEntry Reader ```json theme={null} { "validationResponse": { "status": "failed", "statusDetail": "Subscription expired", "passId": "65844fnrjkfjfkel4h4j23", "readerInstructions": { "message": "Sorry, we can't let you in" } } } ``` **validationResponse** `object` **Properties** **status** `string` *required* Must be either `"successful"` or `"failed"` to indicate validation result **statusDetail** `string` Optional. Human-readable explanation of the validation result, especially useful for failed validations **passId** `string` Optional. The pass identifier if applicable. For example, if `scanValue` includes a pass `ext_id` and promo code (e.g., `123456-HELLO10`), your endpoint can decode the pass `ext_id` and use it as `passId` to associate the scan with a specific pass. **readerInstructions** `object` Optional. Instructions for the reader to display to the user (e.g., messages, future support for LED colors, sounds, etc.) **Properties** **message** `string` Message to display on the reader screen #### VTAP Reader ```json theme={null} { "validationResponse": { "status": "failed", "statusDetail": "Subscription expired", "passId": "65844fnrjkfjfkel4h4j23", "readerInstructions": { "VTAPaction": { "led": "?LEDR 00FF00,100,100,3", "beep": "?BEEPR 50,50,2", "message": "Sorry, we can't let you in", "success": false } } } } ``` **validationResponse** `object` **Properties** **status** `string` *required* Must be either `"successful"` or `"failed"` to indicate validation result **statusDetail** `string` Optional. Human-readable explanation of the validation result, especially useful for failed validations **passId** `string` Optional. The pass identifier if applicable. For example, if `scanValue` includes a pass `ext_id` and promo code (e.g., `123456-HELLO10`), your endpoint can decode the pass `ext_id` and use it as `passId` to associate the scan with a specific pass. **readerInstructions** `object` Optional. Instructions for the reader to display to the user (e.g., messages, future support for LED colors, sounds, etc.) **Properties** **VTAPaction** `object` Optional. Instructions for the VTAP reader (e.g. LED colors, sounds, etc.) **Properties** **led** `string` LED color to display **beep** `string` Beep sound to play **message** `string` Message. Currently not supported **success** `boolean` Whether the validation was successful For more information on VTAP actions, see the [VTAP API documentation](https://www.vtapnfc.com/downloads/VTAP_Commands_Reference_Guide.pdf). # VTAP Automatic Reader Setup Source: https://docs.passentry.com/documentation/guides/vtap-reader/guide Learn how to automatically configure VTAP NFC readers from the PassEntry Dashboard without using a VTAP Cloud account, including network setup and activation steps. VTAP readers can be automatically configured from the PassEntry Dashboard in a few minutes, with PassEntry handling VTAP Cloud configuration on your behalf. *** ## Step-by-Step Instructions ![VTAP Reader Setup](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/vtap-reader/images/vtap-reader-setup.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=31b10a4023ec97ff6b45d5a5c48a316a) 1. [**Navigate to Readers**](https://app.passentry.com/readers) in the PassEntry Dashboard and click **Add reader**. 2. **Fill in Reader Information** * **Reader name**: Choose a name to identify the reader (e.g., `Entrance reader 01`). * **Reader type**: Select **VTAP Reader** from the dropdown. * **Reader ID**: Enter the reader’s serial number (printed on the back of the device, e.g., `CC123456`). 3. **Select Reader Setup Options** * For automatic configuration, choose **Yes** under *Do you want to automatically set up the reader?* * Under *Do you have your own VTAP Cloud Account?*, select: * **No** if using PassEntry’s VTAP Cloud account, or * **Yes** if you bought your readers independently and they are managed through your VTAP Cloud account. * **VTAP Cloud Account**: If using your own VTAP Cloud account, enter the API key (please [contact VTAP support](mailto:vtap.support@dotorigin.com) to get the API key). 4. **Set Location and Network** * **Location**: Provide a label for where the reader is physically located (e.g., `PassEntry HQ`). * **WiFi Credentials**: Enter the network SSID and password to connect the reader to WiFi. 5. **Save Configuration**. The reader will be automatically configured using the information provided. > [!WARNING] > > To receive the updated configuration, the reader must be online. Follow these steps to ensure a proper connection: > > 1. Plug the reader into a laptop or power supply. > 2. Wait for the LED to change from flashing red to flashing blue (3 minutes). Flashing blue indicates the reader couldn’t connect to the internet and is now in hotspot mode. > 3. On your laptop, open the list of available Wi-Fi networks and select the **reader's serial number**, use WPA-PSK security and **password 'passreader'** to connect. > 4. After that you should get redirected to a webpage called ‘Sign in to READER\_SERIAL’. If this does not happen automatically, open a web browser, on the phone or laptop on the 'READER\_SERIAL Setup' network, and go to [http://192.168.4.1](http://192.168.4.1) > 5. Follow the on-screen instructions to manually add a temporary Wi-Fi connection. > 6. Once connected, the reader will receive the configuration and the LED will switch to a steady blue light. > > This confirms the reader is online and has successfully received the updated configuration. *** ## After Setup Once the reader is set up, you’ll see a **“Completed”** status in the Dashboard. > [!WARNING] > > If the setup fails for any reason, please contact [PassEntry Support](mailto:support@passentry.com) — we’ll assist you with troubleshooting. After setup, you can manage the following directly from the PassEntry Dashboard: * Edit the reader's name and location * Update WiFi credentials * Adjust [validation settings](https://app.passentry.com/readers/reader-configuration) All configuration changes are automatically synced with the VTAP reader. # Integrating with PassEntry via Zapier Source: https://docs.passentry.com/documentation/guides/zapier/guide Connect CRM, e-commerce, and marketing tools to PassEntry with Zapier to automate pass issuance, updates, loyalty balances, and wallet events. ![](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/zapier/images/main-image.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=b90a5755644ac9c6cfd771a8a9613c4c) # Zapier Integration Guide With [Zapier](https://zapier.com), you can connect PassEntry to your existing tools like CRMs, e-commerce platforms or marketing automation systems – in just a few clicks. **No code needed.** Build powerful, automated workflows (called Zaps) to issue, update, deactivate, or react to pass events in real time based on customer actions. ![Zapier workflow illustration](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/zapier/images/main.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=726877827cfb6a56ac092d175c3a3a0e) ## When to Use Zapier vs the PassEntry API Use **Zapier** when you want to automate pass workflows from existing business tools like CRMs, e-commerce platforms or marketing systems without writing code. Zapier is ideal for event-driven workflows such as issuing passes when a customer is created, updating passes based on subscription changes or reacting to wallet events. Use the **PassEntry API** when you need full control, custom logic or high-volume operations. The API is better suited for building your own backend integrations, handling complex validation rules or issuing and updating passes at scale. ### Connecting a CRM Such as HubSpot or Salesforce PassEntry does not offer native, pre-built connectors for individual CRMs. There are two supported ways to connect a CRM like HubSpot or Salesforce: * **Via Zapier (no code)**: Select your CRM as the trigger app in a Zap (e.g. **New Contact** or **Form Submission** in HubSpot), then use a PassEntry action such as **Create Pass**. The workflow below walks through this setup. * **Via the PassEntry API (custom build)**: Use the webhooks or API of your CRM to call PassEntry's REST endpoints from your backend, authenticating with your API key. See [Authentication](/api-reference/v1/introduction/authentication) and [Create Pass](/api-reference/v1/passes/create-pass). *** ## What You Can Do Use PassEntry's Zapier app to: * **Issue passes** to users from your CRM or e-commerce platform * **Update pass data** (name, status, balance, barcode, location, etc.) * **Receive real-time pass status updates** (e.g. added to wallet, removed, scanned) * **Manage loyalty balances** (add/redeem/override points) * **Deactivate passes** when users unsubscribe, churn, or complete an event *** ## 🛎️ Available Triggers ### `Pass Created` Fires when a new pass is issued in PassEntry. Perfect for sending welcome emails, creating CRM records, or starting onboarding workflows. ### `New Pass Added to Wallet` Fires when a pass is added to a user’s Apple Wallet or Google Wallet. Ideal for tracking activations or triggering follow-up actions, e.g. sending a welcome push notification. ### `Pass Removed From Wallet` Fires when a user removes a pass from their wallet. Can be used for re-engagement campaigns or tracking churn. ### `Pass Scanned` Fires when a pass is scanned using NFC or barcode. Works well for check-in systems, loyalty programs, and access control. ### `Pass Status Changed` Fires when a pass is added to or removed from a customer’s wallet. Covers both events in one trigger – useful for monitoring wallet activity. *** ## 🛠️ Available Actions ### `Create Pass` Issues a new pass using a selected pass template. You’ll map values from your CRM or another app into the pass fields. > [!WARNING] > > Before setting up your Zap, please make sure to create a pass template via PassEntry API or Dashboard. This template will appear in the **Pass Template** dropdown field in Zapier. ### `Update Pass` Updates specific fields for an existing pass based on External ID. Only include the values you want to change – everything else will remain unchanged. > [!WARNING] > > If you use multiple pass templates (e.g. for different membership tiers), set up a separate **Zap path** or **Zap** for each one. > Each path must use the correct template ID – changes made using a mismatched template will be rejected. > This ensures the right dynamic fields are used and your updates go to the correct pass type. ### `Update Loyalty Balance` Adds, redeems, or overrides a pass’s loyalty balance. Ideal for e-commerce and reward engines. > [!WARNING] > > Make sure to enable **Loyalty Balance** when creating your pass template in PassEntry. > Also, choose the correct number type – **integer** or **decimal** – based on the values your system will send. > If the types don't match, the update may fail. ### `Deactivate Pass` Marks a pass as deactivated. Use this when: * A subscription ends * An event finishes * A user opts out *** ## 🔍 Searches ### `Search Pass` Looks up a pass using an External ID or UUID. Returns full pass details including status and download link. Use this as an intermediate step in a Zap when: * You need to **retrieve the template ID** before performing a dynamic update (e.g. mapping fields based on the pass structure). * You want to **verify that a pass exists** before attempting to update or deactivate it. * You want to **branch workflows** based on the pass status (e.g. only continue if the pass is active). * You need to **retrieve the download URL** to send it via email or SMS. * You want to use **pass metadata** (template name, balance, expiration, etc.) in downstream actions. *** ## 🪄 Set Up Your First Workflow *Set up a simple Zapier workflow to issue a digital pass when a new contact is created in your CRM.* ### 📝 Requirements * A PassEntry account with at least one published pass template. * Zapier account (free or paid depending on your task volume and complexity). * API Key from PassEntry Dashboard. *** ### 1. Create a Pass Template in PassEntry * Log into your [PassEntry Dashboard](https://app.passentry.com) * Create a new pass template (e.g. "Gold Membership", "Event Ticket") if you don't have one \[!WARNING] Make sure the template includes all the fields you want to populate from your CRM, like name, membership level, loyalty balance etc. * Head to [Developer Tools](https://app.passentry.com/developer-tools) and copy your API Key ![API key retrieval](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/zapier/images/bearer-token.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=8c55052725c9d6773502d3e366526ab3) *** ### 2. Create a New Zap * Go to [Zapier](https://zapier.com) and click **Create Zap** * Choose your trigger app (e.g. Salesforce, HubSpot, Google Sheets) * Select a trigger event, such as **New Contact**, **New Row**, or **Form Submission** ![Trigger Setup on Zapier](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/zapier/images/trigger.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=1dcce8ade2eafa18cff3cf6c9d2807f5) *** ### 3. Add the “Create Pass” Action * Choose the **Create Pass** action from the **PassEntry** app * Select the pass template you created in Step 1 * Zapier will automatically load dynamic fields based on your template ![Action Setup on Zapier](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/zapier/images/action.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=a0661fca1307f24999dca2fd27eeb128) *** ### 4. Map Your Fields * Match data from your trigger app to PassEntry’s fields (e.g. first name, tier, expiration date) * Most importantly, map the **External ID** to a unique ID in your system (CRM user ID, email, etc.) ![Map your data on Zapier](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/zapier/images/mappings.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=4e49479140da29b9c9e45ac65ff743b9) *** ### 5. Test and Launch * Use **Test Step** to preview a pass creation * If successful, Zapier will return a download link * Click **Publish** to activate the Zap ![Create Action Output](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/guides/zapier/images/output.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=d467f3fe823eccdf378704ab48d5d852) > [!WARNING] > > You can also build multi-step Zaps and use the returned **download link** to distribute the pass via SMS or email, or to update a CRM record. This allows you to fully automate your pass delivery process. *** ## ✅ Wrapping Up You’re now ready to start building powerful automations with PassEntry and Zapier. We recommend starting with a simple use case – like issuing a pass when a new customer is created – and then gradually exploring more advanced flows like pass updates or reacting to real-time events. We’ll continue adding more examples and case studies to help you get the most out of the integration. If you have any questions or want help designing your first Zap, feel free to [contact our support team](mailto:support@passentry.com) – we’re here to help! # Pass template types: layouts, fields, and use cases Source: https://docs.passentry.com/documentation/pass-template-types-explained Understand PassEntry pass template types, how they differ in layout and supported fields and why they do not restrict your use case. ## Overview Pass template types in PassEntry define **layout structure, available visual assets and field placement**. They do **not** limit how you can use a pass. You can use any template type for any scenario, whether that’s loyalty, access control, events, memberships, gift cards or insurance passes. The template type simply gives you a starting layout optimised for a common use case. ### In this guide * [Do pass template types restrict use cases?](#do-pass-template-types-restrict-use-cases) * [Available template types](#available-pass-template-types) * [Visual examples](#visual-examples-of-pass-template-types) * [Comparison table](#template-type-comparison) * [How template types differ](#how-template-types-differ) * [Draft and published templates](#draft-and-published-templates) * [Choosing the right template type](#choosing-the-right-template-type) * [FAQ](#frequently-asked-questions) * [What’s next](#what’s-next) *** ## Do pass template types restrict use cases? A pass template type: * ✅ Controls **layout** * ✅ Determines which images and fields are available or required * ❌ Does **not** enforce business logic or restrict usage For example: * A **Loyalty** template can be used for access control * An **Event** template can also be used for non-ticket scenarios that require more visible fields or broader geo-based notifications. * A **Generic** template can be used for almost any scenario Choose the template type based on **how you want the pass to look**, not how you name or market the pass. *** ## Available pass template types PassEntry currently supports the following template types: * Loyalty * Generic * Stamp card * Event * Transit Each type offers a different visual structure and set of configurable assets. *** ## Visual examples of pass template types The examples below show typical layouts for each pass template type. They illustrate visual structure only. You can customise and reuse any template for different use cases. #### Loyalty ![Loyalty pass template example](https://mintcdn.com/passentry-0/wVHc_KE_FnB4W_b6/documentation/images/template-types/loyalty.png?fit=max\&auto=format\&n=wVHc_KE_FnB4W_b6\&q=85\&s=d210615e54dedde36c0ccbbd4b7adf51) Primary information displayed prominently at the top of the pass. #### Event ![Event pass template example](https://mintcdn.com/passentry-0/wVHc_KE_FnB4W_b6/documentation/images/template-types/event.png?fit=max\&auto=format\&n=wVHc_KE_FnB4W_b6\&q=85\&s=85fc4498f5fab560489b39079e760656) Supports a higher number of detail fields on the front of the pass. #### Generic ![Generic pass template example](https://mintcdn.com/passentry-0/wVHc_KE_FnB4W_b6/documentation/images/template-types/generic.png?fit=max\&auto=format\&n=wVHc_KE_FnB4W_b6\&q=85\&s=84c9b524e879edc1a433aca54b2eff19) Includes a central field ("Guest Name") and uses a thumbnail image on Apple Wallet. #### Stamp card ![Stamp card pass template example](https://mintcdn.com/passentry-0/wVHc_KE_FnB4W_b6/documentation/images/template-types/stamp-card.png?fit=max\&auto=format\&n=wVHc_KE_FnB4W_b6\&q=85\&s=6b4142af309e8723cb9e6e49d7d816a3) Progress-based layout showing completed and remaining steps (4 cups out of 7 filled – 3 more to go). #### Transit ![Transit pass template example](https://mintcdn.com/passentry-0/wVHc_KE_FnB4W_b6/documentation/images/template-types/transit.png?fit=max\&auto=format\&n=wVHc_KE_FnB4W_b6\&q=85\&s=ea7c3c0821337d21ddb93f9bf33cb395) Requires two central fields (origin and destination) and emphasises route or journey details. *** ## Template type comparison The table below contains the main differences between pass template types including available assets, fields and features. All types support Apple Wallet and Google Wallet. | Feature | Loyalty | Generic | Event | Transit | | - | - | - | - | - | | Horizontal logo | ✓ | ✓ | ✓ | ✓ | | Square icon | ✓ | ✓ | ✓ | ✓ | | Thumbnail | – | Apple Wallet only | – | – | | Banner | ✓ | Google Wallet only | ✓ | Google Wallet only | | Header fields | 1 | 1 | 1 | 1 | | Central fields | – | 1 | – | 2 | | Details fields | 4 | 6 | 6 | 6 | | Back fields | 5 | 5 | 5 | 5 | | Geo-based **notifications radius** limit | 100m | 100m | **1000m** | 100m | | Per-pass **background colour override** | Apple Wallet only | ✓ | ✓ | ✓ | ## How template types differ ### Layout and field structure Template types define: * Which field sections are available (header, central, details, back) * How many fields can appear in each section * Whether certain sections are required For example: * **Transit** templates require two central fields. * **Event** templates allow more detail fields on the front, but don’t have a central field. * **Generic** templates include one central field. *** ### Images and branding assets Template types also control which images are supported or required, such as: * Logo * Icon * Banner * Thumbnail Some template types support banners across both wallets, while others use only icons or logos depending on the platform. This keeps your passes compliant with Apple and Google design requirements while giving you flexibility to brand them. *** ## Draft and published templates Save a pass template as a **draft** while you're still iterating on it — try out different layouts, fields, colours or images before making the template available to issue passes. **Why use drafts?** * Experiment with different pass template ideas side by side without polluting your published catalogue * Iterate on a design privately until it's ready * Publish a draft when you're happy with it; delete the ones you no longer need * Drafts are capped at 20 per organisation, but **don't count against your pass template quota** — keep a working set as you experiment | Status | Meaning | | - | - | | `published` | Ready to use. Passes can be issued from this template. | | `draft` | In progress. Cannot be used to issue passes until published. | The `status` field appears on every pass template response under `data.attributes.status`. > [!NOTE] > > Drafts are created and managed through the PassEntry Dashboard. *** ## Choosing the right template type > [!NOTE] > > There is no single “correct” template type. The best choice depends on the layout and fields you need to display. When selecting a template type, ask: * How many fields do you need to display on the front and on the back of the pass? Remember that clickable links must be placed on the back of the pass — see [Can I add a clickable link to a back field?](#can-i-add-a-clickable-link-to-a-back-field) below. * Which layout best fits the information you want to display? * Do you need a central field, two central fields or a progress-based layout? * Which assets (logo, banner, icon, thumbnail) do you want to highlight? Do **not** worry about: * The name of the template type * Whether your use case matches the label exactly *** ## Frequently asked questions ### Do pass template types restrict how a pass can be used? No. Pass template types only define layout, available assets and field structure. You can use any template type for any use case. ### Which pass template type should I choose? Choose the template type based on the layout and fields you need to display on the pass, not on the name of the template type. ### Can I use a Loyalty template for non-loyalty use cases? Yes. For example, Loyalty templates are often used for access passes or memberships where a membership ID is displayed in the top-right header field. ### If I want to use the Loyalty API and update a balance on the pass, do I need to use a Loyalty template? Not necessarily. Any template type can be configured to use a header field as a balance field and apply loyalty balance formatting. Once the template is configured this way, you can use the Loyalty API with any template type. ### Do all template types support Apple Wallet and Google Wallet? Yes. All pass template types support both Apple Wallet and Google Wallet, but the available assets (such as banners or thumbnails) may vary slightly. ### Can I change the template type later? You can change the template design (colours, fields, images) at any time, but the template type itself cannot be changed after creation. ### I updated a field's default value on a template — why hasn't it changed on existing passes? Changing a field's default value on a template only applies to **newly created passes** by default. Existing passes keep whatever value they already have on that field. * **Dashboard:** when you change a field's default value on a template that already has passes, a required prompt appears — *"Who should this change apply to?"* — with two options: **New passes only** (default) or **New and existing N passes**. Choose **New and existing N passes** to push the new default out to every existing pass on the template. * **API:** set `overrideFieldValue: true` on the field in your [Update Pass Template](/api-reference/v1/pass-templates/update-pass-template) request. See the [pass templates guide](/api-reference/v1/pass-templates/guide) for a full example. Other template-level changes (colours, images, field labels) are applied to all existing passes automatically when the template updates. To change field values on individual passes without touching the template default, use the [Update Pass API](/api-reference/v1/passes/update-pass) or [Batch Pass Update](/documentation/bulk-operations/batch-pass-update/guide). ### Can I add a clickable link to a back field? Yes. Clickable links are only supported on **back fields** — front fields (header, central, details) render as plain text on both Apple Wallet and Google Wallet. Bare `https://` URLs placed in a back field's value are automatically formatted as clickable links on both Apple Wallet and Google Wallet — no `` tag required. For custom link text or to list multiple links in a single field, back fields also render HTML anchor tags and newline characters on both platforms. Wrap each URL in an `` tag and separate entries with `\n`: ```html theme={null} FAQs\nCONTACT SUPPORT\nTERMS & CONDITIONS ``` The anchor text is what appears on the pass, and each `\n` renders as a line break. URLs must begin with `http://`, `https://`, or `mailto:`. When supplying HTML anchors via CSV (batch pass creation or update), follow the CSV escaping rules described in the [batch pass creation guide](/documentation/bulk-operations/batch-pass-creation/guide). *** ## What’s next? * Learn which [design assets to use for your pass template](/documentation/passes/design/guide) * Explore [issuing passes from template via API](/api-reference/v1/passes/create-pass) *** ## Need help? If you’re unsure which template type to choose, contact [PassEntry support](mailto:support@passentry.com). We’ll help you pick the best starting point for your use case. # Apple Poster Designs Source: https://docs.passentry.com/documentation/passes/apple-poster-designs/guide Give Apple Wallet passes a full-bleed poster face, with the generic poster design, its artwork and field layout, and up to two featured action buttons. > [!WARNING] > > Poster designs are in **closed beta**. The payload shape may still change. [Contact PassEntry](mailto:support@passentry.com) for access. Apple's poster designs replace the standard pass face with a full-bleed image, your fields laid over it, and an information panel underneath. PassEntry supports two of them, set on the pass template through `appleEnhancedDesign.type`: | Design | What it is | Template types | Extra entitlements | | - | - | - | - | | `eventTicket` | The poster event ticket, with event details, seating and an event guide | Event ticket | NFC and pass binding | | `generic` | A poster face with your own fields and up to two action buttons | Event ticket, generic, loyalty | None | This guide covers the `generic` design. For the event ticket design, see the `enhancedData.eventTicket` fields on [Create Pass](/api-reference/v1/passes/create-pass). ## One pass, every device The generic poster renders on **iOS 27 and later**. Older versions of iOS cannot read the poster layout at all, so every pass carries a second copy of the same fields in its template's standard layout. An iPhone on iOS 26 shows the standard pass, an iPhone on iOS 27 shows the poster, and you issue one pass either way. The layout a device falls back to follows the template type: a generic template falls back to a generic pass, a loyalty template to a store card, an event ticket template to an event ticket. ## Prerequisites * Your organisation has poster designs enabled. [Contact PassEntry](mailto:support@passentry.com) if you need access. * Your template is an event ticket, generic or loyalty template. Stamp card and transit templates cannot use a poster design. * You have artwork ready for the poster background. Artwork is required, including when all you want is the action buttons. ## Enabling the design Set the design type and upload the artwork on the pass template. ```json Enable the generic poster theme={null} PATCH /api/v1/pass-templates/YOUR_TEMPLATE_UUID { "passTemplate": { "appleEnhancedDesign": { "type": "generic", "images": { "artwork": "https://www.example.com/artwork.png" } } } } ``` | Parameter | Required | Description | | - | - | - | | `appleEnhancedDesign.type` | Yes | `generic` for the poster face described here. Send `null` to turn the poster off and return the template to its standard layout. | | `appleEnhancedDesign.images.artwork` | Yes | Poster background. Maximum 1074x1344 px. Keep important content away from the top and bottom edges, which sit under the header and chin regions. | PassEntry rebuilds passes already issued from the template with the new design and pushes them to the wallet. The `secondaryLogo` and `venueMap` images belong to the event ticket design. The generic poster has no event guide to show a venue map in, and takes its chin logo from the template's standard logo image. ## What the poster shows The poster face has fewer slots than the standard layout, so PassEntry maps your existing fields into it rather than asking you to author them twice. | Poster slot | Filled from | | - | - | | Header | Your first header field | | Main fields | Up to three fields, taken from your central fields first, then your details fields, in the order they appear on the template | | Information panel | Everything that did not fit above | | Back of the pass | Your back fields, unchanged | Nothing is dropped. A field that overflows the poster face moves into the information panel underneath it, and the same fields keep their usual places on devices that render the standard layout. The template's colours apply to the poster as you set them. ## Featured actions Featured actions are up to two buttons under the poster. Each one opens a URL, and Apple renders the button from the action type you choose. Author them on the template, in the `one` and `two` slots: ```json Add featured actions theme={null} PATCH /api/v1/pass-templates/YOUR_TEMPLATE_UUID { "passTemplate": { "appleEnhancedDesign": { "type": "generic", "featuredActions": { "one": { "type": "viewSchedule", "url": "https://www.example.com/schedule" }, "two": { "type": "viewOffersRewards", "url": "https://www.example.com/offers" } } } } } ``` Rules: * Filling an empty slot needs both a `type` and a `url`. PassEntry rejects a slot that has only one of them. * `url` must be an `https://` URL. * The two slots must use different types. * `featuredActions` is only accepted when `type` is `generic`. Sending it on the event ticket design returns a `422`. ### Action types Apple defines the full set and renders each one its own way. Pick the one that matches where the button leads. | Type | Use it for | | - | - | | `viewSchedule` | A schedule, line-up or agenda | | `watchTrailer` | A trailer or preview video | | `listenToMusic` | A playlist, album or track | | `call` | A page where someone can reach you by phone | | `place` | A venue or location page | | `addToBalance` | Topping up a balance | | `order` | Ordering food, drink or merchandise ahead | | `shop` | An online store | | `membershipBenefits` | What a membership includes | | `bookAppointment` | Booking an appointment | | `bookCar` | Booking a car or transfer | | `bookFlight` | Booking a flight | | `bookStay` | Booking somewhere to stay | | `viewOffersRewards` | Current offers or rewards | Every action takes an `https://` URL, so `call` points at a web page rather than a `tel:` link. ### Changing a slot later Send only the slot you want to change. An omitted slot keeps whatever it holds. ```json Repoint slot two theme={null} PATCH /api/v1/pass-templates/YOUR_TEMPLATE_UUID { "passTemplate": { "appleEnhancedDesign": { "featuredActions": { "two": { "url": "https://www.example.com/new-offers" } } } } } ``` To remove a slot, send it as `null`: ```json Remove slot two theme={null} { "passTemplate": { "appleEnhancedDesign": { "featuredActions": { "two": null } } } } ``` ## Per-pass artwork The poster background comes from the template, and every pass inherits it. To give one pass its own poster, send `artworkImage` under `templateOverride` when you create or update the pass. ```json Give one pass its own poster theme={null} POST /api/v1/passes?passTemplate=YOUR_TEMPLATE_UUID { "pass": { "templateOverride": { "artworkImage": "https://www.example.com/artwork/jane.png" } } } ``` Send an HTTPS URL or a base64 data URI, or point at an image you have already uploaded with `{"imageAssetId": "YOUR_IMAGE_ASSET_ID"}`. The 1074x1344 px limit still applies. Send `null` to drop the override and go back to the template's artwork. ## Per-pass actions A pass takes a copy of the template's featured actions when it is created, so most passes need nothing extra. To point one pass somewhere else, send the slot under `enhancedData.generic` on the pass. ```json Override an action for one pass theme={null} POST /api/v1/passes?passTemplate=YOUR_TEMPLATE_UUID { "pass": { "enhancedData": { "generic": { "featuredActions": { "one": { "url": "https://www.example.com/schedule/vip" } } } } } } ``` The same body works on [Update Pass](/api-reference/v1/passes/update-pass). Slot rules match the template: send both keys to fill an empty slot, send one key to change one value, send `null` to remove the slot, and omit the slot to leave it alone. Types must still differ across the two slots once your change is applied. > [!NOTE] > > After a pass is created, its own actions win. Editing the template's featured actions changes new passes, not existing ones, so a per-pass override is never overwritten later. ## What's next * [Create Pass Template](/api-reference/v1/pass-templates/create-pass-template) for the full template payload * [Create Pass](/api-reference/v1/passes/create-pass) for per-pass data * [Design Guide](/documentation/passes/design/guide) for image requirements across both wallets # Design Guide Source: https://docs.passentry.com/documentation/passes/design/guide Learn how to design high-quality Apple Wallet and Google Wallet passes with the right image formats, sizes and platform-specific best practices to ensure your passes look sharp and consistent across devices. # Images Design plays a big role in how your passes are perceived in Apple Wallet and Google Wallet. Using the right image formats, sizes and layouts ensures your passes look sharp, load quickly and behave consistently across devices. This guide walks through image requirements and design best practices for both platforms. > [!NOTE] > > Image requirements and placement can vary depending on the pass template type. For more details, see the [Pass Template Types Explained](/documentation/pass-template-types-explained) guide. ## Best Practices When uploading images, please ensure they're in the `PNG` format, and that they're of a high quality. We recommend compressing images using [Squoosh](https://squoosh.app/), and OxiPNG. We accept images as direct URLs, or as base64 encoded data. When using base64 encoded data, please ensure the data is URL safe. When using direct URLs, please ensure the URL is publicly accessible. Use different images for each platform, and overlay text with instructions on how to get to the back of the pass. We validate the image format and size when you upload an image. ## Shared Images To facilitate the creation of passes, we accept images that are shared across multiple platforms. Please note that in some cases, images may be cropped or resized to fit the platform's requirements, so for best results, we recommend uploading variants optimized for each platform. These are: | Image | Size | Used for | | - | - | - | | Banner | 1125px x 432px | Apple Banner, Google Banner | | Icon | 660px x 660px | Apple Notification Icon, Google Logo | | Logo | 480 x 150px | Apple Logo | ## Apple Images ### Banner ![Apple Banner](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/passes/design/apple_banner.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=50e311c3615ca13629ed78fb3e595379) When present, it appears prominently on the Apple Wallet. Use it to wisely to promote your brand or product. If including text, please ensure it's readable and contrasted against the banner image background. **Sizing varies depending on template type:** | Template Type | Banner Size | | - | - | | Generic | Not present | | Loyalty | 1125px x 432px | | Event Ticket | 1125px x 294px | | Loyalty Stamp Card | 1125px x 432px | ## Logo ![Apple Logo](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/passes/design/apple_logo.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=d817a3f0f1b892ee7023775749b9aaf3) Appears on the top left of the pass. Ensure the logo is readable and contrasted against the background colour of the pass. If you would like a logo narrower than 150px, simply add a transparent space to the right of the logo. **Size:** 480px x 150px ## Icon ![Apple Icon](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/passes/design/apple_icon.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=a009b38bb3f35bbe185eb757274db231) This appears on the notification tray when receiving notifications, or when the device is within a geofence. **Size:** 87px x 87px > [!NOTE] > > iOS devices cache the notification icon, so if you update the icon, it may take some time until the new icon is displayed. ## Thumbnail ![Apple Thumbnail](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/passes/design/apple_thumbnail.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=8f8d97fc3296693c38d2833918d86fba) Only appears in "Generic" template types. This appears on the Apple Wallet when the pass is viewed. **Size:** 270px x 270px ## Google Images ### Banner ![Google Banner](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/passes/design/google_banner.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=7ad8ea9dda75f4dfd0b1bcee1f7fb77b) When present, it appears prominently on the Google Wallet. Use it to promote your brand or product. If you want the background colour of the pass to show through, use a transparent PNG. **Size:** 1032px x 812px (square, recommended) or 1032px x 336px (wide) **Do:** * Use high-resolution photography or illustrations * Include padding on the top and bottom for visual breathing room **Don't:** * Embed text into your images — this content won't be localised * Have the main content of the image touching the top and bottom edges ### Logo ![Google Logo](https://mintcdn.com/passentry-0/Pme8e7kS-lNx7reF/documentation/passes/design/google_logo.png?fit=max\&auto=format\&n=Pme8e7kS-lNx7reF\&q=85\&s=bce5c9f998d1113199812ad81c1040e2) Google Wallet masks your logo into a circular shape. Ensure it has enough margin (15%) to fit into the safe area, otherwise it may be cropped when masked. Don't pre-mask your logo — leave it in a square with a full bleed background colour. ![Google Logo Safe Area](https://mintcdn.com/passentry-0/Pme8e7kS-lNx7reF/documentation/passes/design/google_logo_guide.png?fit=max\&auto=format\&n=Pme8e7kS-lNx7reF\&q=85\&s=722edcdce46013c4c5e1a62238dba6ce) **Size:** 660px x 660px ### Wide Logo ![Google Wide Logo](https://mintcdn.com/passentry-0/Pme8e7kS-lNx7reF/documentation/passes/design/google_wide_logo.png?fit=max\&auto=format\&n=Pme8e7kS-lNx7reF\&q=85\&s=03071c4f9d809c19f65c742d675b635e) An alternative to the circular logo. When set, it replaces the round logo on the front of the pass. Use a wide, rectangular image. **File type:** Transparent PNG **Size:** 1280px x 400px (minimum height: 400px) **Aspect ratio:** 16:5 ## PassEntry Watermark ![PassEntry Watermark](https://mintcdn.com/passentry-0/dT3buMXxEcyjrFvh/documentation/passes/design/banner_with_watermark.png?fit=max\&auto=format\&n=dT3buMXxEcyjrFvh\&q=85\&s=ea7153fc85e0c6c462898d4e38becb56) ![Apple Logo](https://mintcdn.com/passentry-0/nVmEPjVSxA1ig75q/documentation/passes/design/passentry_watermark_dark.svg?fit=max\&auto=format\&n=nVmEPjVSxA1ig75q\&q=85\&s=cb842993cfe8906c024edd66bdbb1b72) When uploading a banner image, we automatically add a watermark to the image. You can choose whether to use the dark or light version, and the position of the watermark. To remove the watermark, contact your sales at PassEntry. # Downloading Passes Source: https://docs.passentry.com/documentation/passes/downloading-passes/guide Deliver passes with PassEntry's hosted download page or direct Apple Wallet and Google Wallet pass source delivery, and customise the download page. Once a pass is created, the next step is getting it into the user’s wallet. PassEntry offers two delivery options: using a **hosted download page** or delivering the **pass source directly**. The right approach depends on whether you know the user’s device type and where the delivery happens in your flow. Use the hosted download page when you want a simple, device-agnostic way to share passes via a single link. Use direct pass source delivery when the user’s device is already known – for example, inside a mobile app or a device-aware web experience. ## Using PassEntry's Download Page When creating a pass, we return a JSON object with pass attributes and timestamps. Within this object, we include a `downloadUrl`, unique to each pass, which when clicked by the end user will open a generic pass download page, a web view hosted by PassEntry on `download.passentry.com`. As at this stage, the passholder's device type is unknown, one of the purposes of this download page is to detect the user's device and deliver either an Apple Wallet pass or Google Wallet pass. When the download page is opened on a device other than an iPhone or Android phone, it will display a QR code directing the user to the same URL, in order to direct them to open the download page on a mobile device. Using this download page enables simple distribution of passes to your customers via a generic, dynamic link. As a best practice, we suggest putting the `downloadUrl` behind a button or redirecting the user to it at a desired point in their user journey. ### Customising the Download Page You can customise the pass download pages for either your organisation, an entity (if applicable) or one or more pass templates. The options to create, edit or delete pages are found under the *Pass download pages* tab on the PassEntry Dashboard. Upload brand-specific images, choose custom colours, and personalise text to ensure each page closely aligns with your own platform. ### Adding a Help and FAQs Link You can display a help and FAQs link on your download page to point users to troubleshooting information while they download their pass. To enable it: 1. Open the *Pass download pages* tab on the PassEntry Dashboard and create or edit a page. 2. In the **Links** section, turn on the **Display help and FAQs link** toggle. 3. Enter the **Help and FAQs text** (the link label shown on the page) and the **Help and FAQs URL** (the destination, such as your support or FAQ page). Note: When a pass is downloaded, the system will display a custom page based on the following priority: 1. If a custom page exists for the pass template, that page will be shown. 2. If no template-specific page is found but a custom page exists for the associated entity, the entity page will be shown instead. 3. If neither a pass template page nor an entity page is available, your organisation's default page will be displayed. ![](https://mintcdn.com/passentry-0/Pme8e7kS-lNx7reF/documentation/passes/downloading-passes/create-pass-download-page.png?fit=max\&auto=format\&n=Pme8e7kS-lNx7reF\&q=85\&s=70485b364ea9106a758ef22c8668e379) ![](https://mintcdn.com/passentry-0/Pme8e7kS-lNx7reF/documentation/passes/downloading-passes/pass-download-pages.png?fit=max\&auto=format\&n=Pme8e7kS-lNx7reF\&q=85\&s=3ec3a8fa824461218291e094a937145b) ![](https://mintcdn.com/passentry-0/Pme8e7kS-lNx7reF/documentation/passes/downloading-passes/edit-pass-download-page.png?fit=max\&auto=format\&n=Pme8e7kS-lNx7reF\&q=85\&s=57de11b2284be6fb960adbc535061f25) ## Using 'Direct' Pass Source Delivery In some cases, use of the download page is not a viable option, such as when provisioning the pass from within an app, or when wanting to directly deliver the pass from within a web page, without redirecting the user to PassEntry's download page. In order for this to be possible, you must know the device type when creating the pass. When delivering the pass from within an app, the device type will already be known. When delivering from a web page, there are a number of scripts that can be used to detect the device type and request the correct pass source. Here is a sample script: ```javascript Fetching the device type theme={null} const getMobileOS = () => { const userAgent = navigator.userAgent || navigator.vendor || window.opera; if (/android/i.test(userAgent)) { return "Android"; } else if (/iPhone/i.test(userAgent)) { return "iPhone"; } return "Other"; }; // Example usage const mobileOS = getMobileOS(); console.log(`This page is opened on: ${mobileOS}`); ``` Once the device is known, request and deliver the correct Pass Source. To request the Pass Source, include the `?includePassSource` query parameter in your `POST /passes/` or `GET /passes/:id` request. This parameter accepts `apple` or `google` as options. For more details, review the [Create Pass API](/api-reference/v1/passes/create-pass) and [Get Pass API](/api-reference/v1/passes/show-pass) documentation. If valid, the response will include the pass attributes in JSON, alongside a `passSource` object. It will only return the `passSource` object in the response if the selected pass is not currently active in a device. **Delivering a Google Wallet Pass** The `passSource` object will contain a key, `googleDownloadUrl`, containing a unique URL, which when opened on an Android device, will prompt the user to add their pass to their Google Wallet. As the Google Wallet is linked to an individual Google Account, the user will be prompted to log in to their Google Account if they are not already logged in and to download the Google Wallet if they have not already done so. The `googleDownloadUrl` works best when placed behind Google's "Add to Google Wallet" button. Here are the brand guidelines and assets provided by Google: [Add to Google Wallet](https://developers.google.com/wallet/generic/resources/brand-guidelines?hl=en) **Delivering an Apple Wallet Pass** Apple requires the pass source to be delivered as a file with a `.pkpass` extension. The `passSource` object will contain a key, `appleBase64`, containing the `.pkpass` file encoded in base64. Decode the base64, save it to a file with the `.pkpass` extension, and deliver this to the user. When an iPhone detects a `.pkpass` file, it will prompt the user to add it to their Apple Wallet. Deliver the pass when the user clicks Apple's "Add to Apple Wallet" button. Here are the brand guidelines and assets provided by Apple: [Add to Apple Wallet](https://developer.apple.com/wallet/add-to-apple-wallet-guidelines/) # Geofencing and iBeacons for wallet pass notifications Source: https://docs.passentry.com/documentation/passes/geofencing/guide Learn how to use geofencing and iBeacons to trigger location-based behaviour for Apple Wallet and Google Wallet passes, including platform-specific limitations. **Geofencing** lets your passes show up at exactly the right moment, when a customer is near your store, venue or checkpoint. Instead of relying on emails or SMS, the **wallet surfaces the pass automatically based on location**. With PassEntry, you can use **GPS-based geofences** for broader areas like stores or venues or **iBeacons** for precise, short-range interactions such as entrances, counters or specific sections inside a location. ## Geofence notifications * When a user enters a geofenced area, a notification appears on the lock screen, displaying a preview of the pass from the wallet app along with any set geofence message. > [!NOTE] > > Custom geofence messages are supported only on Apple Wallet. On Google Wallet, > the pass is surfaced using the standard notification header and icon, without a > custom message. Google Wallet uses its own text: the notification title is the > pass or template identifier (for example, `member-pass`) and the body is a > generic prompt such as `Nearby`. The heading and message you set in the template > are ignored on Android. There is no workaround for this on Google Wallet. * Tapping the notification opens the pass in the wallet app, giving quick access to barcodes or NFC validation. * Geofence notifications are **passive**: the wallet does not notify PassEntry when a user enters a geofence. This design respects privacy and security considerations. * Geofence notifications remain active and visible until the user leaves the geofence. **Note:** When you set two or more geofences in close proximity, the notification may appear with a delay when the user moves between geofences. The device implements a cooldown period to ensure that the user has left the previous geofence before entering the next one. You can easily configure geofences using our interactive Pass Template builder: ![](https://mintcdn.com/passentry-0/Pme8e7kS-lNx7reF/documentation/passes/geofencing/geofencing.png?fit=max\&auto=format\&n=Pme8e7kS-lNx7reF\&q=85\&s=3cba46087e3746b6e868a9047bb75bde) ## Locations * Uses exact coordinates (latitude and longitude) to define the targeted area. * You can set the geofencing radius between 10 and 1000 metres (depending on pass type). Tailor the geofenced area to your needs, such as covering a small store or a larger outdoor venue. > [!NOTE] > > Geofence radius configuration is supported on Apple Wallet passes only. * Each pass can include up to 10 geofenced locations. For more extensive coverage, consider using **beacons**. * PassEntry cannot track the exact location of the user. PassEntry does not have access to the user's location data, as this never leaves the device. ## Google Wallet geofence radius > [!WARNING] > > Google's effective notification radius is determined by Google and may be between 50m and 1000m. The exact value is based on a non-public heuristic, enforced by Google and subject to change without notice. When configuring geofence locations for Google Wallet passes, Google applies its own logic to determine the effective radius, meaning the actual notification trigger area may differ slightly from your configured value. ## Beacons > [!NOTE] > > iBeacons are supported only on Apple Wallet passes. iBeacons are a technology standard developed by Apple that lets mobile apps listen for signals from beacons in the physical world and react accordingly. The technology is based on Bluetooth Low Energy (BLE) proximity sensing, which enables a device to perform actions when in close proximity to a beacon. PassEntry cannot track the exact location of the user. PassEntry does not have access to the user's location data, as this never leaves the device. Learn more about beacons here: [iBeacon](https://en.wikipedia.org/wiki/IBeacon) ## Testing beacons without hardware You do not need to purchase a physical beacon to test iBeacon behaviour on your pass. Apps such as [Beacon Walker](https://apps.apple.com/us/app/beacon-walker-tool-for-ibeacon/id1161618906) (available for iOS and macOS) can broadcast a configurable iBeacon signal from one device, which you can then detect on another device holding the pass. To test: 1. Install Beacon Walker on a spare iOS or macOS device. 2. Configure it to broadcast using the same `proximity_uuid`, `major` and `minor` values defined on your pass. 3. Add the pass to Apple Wallet on a second device and bring it within range of the broadcasting device. If you want to enable geofence capabilities for a large number of locations, here's an example: **Use Case: PassEntry Coffee Chain** * Scenario: PassEntry Coffee, a chain with 100 locations across London, Dubai, and Singapore, uses iBeacons to offer location-based promotions. * Each location is equipped with two beacons: one in the bakery section and another in the cafe section. * The PassEntry Coffee loyalty pass runs different campaigns across regions, using the same UUID for all beacons but varying Major and Minor values for location and section identification. * In total, PassEntry Coffee would purchase and configure 200 iBeacons (100 locations x 2 sections). Configuration Example * UUID: All beacons use E2C56DB5-DFFB-48D2-B060-D0F5A71096E0. * Major: Represents the city (e.g., 1 for London, 2 for Dubai). * Minor: Specifies the section within a location (e.g., 1 for the bakery, 2 for the cafe). In this example: **PassEntry Coffee** would configure their 200 beacons like so: ``` London (50 locations): Bakery Section beacons: uuid: E2C56DB5-DFFB-48D2-B060-D0F5A71096E0 major: 1 minor: 1 name: 'london-bakery' Cafe Section beacons: uuid: E2C56DB5-DFFB-48D2-B060-D0F5A71096E0 major: 1 minor: 2 name: 'london-cafe' Dubai (30 locations): Bakery Section beacons: uuid: E2C56DB5-DFFB-48D2-B060-D0F5A71096E0 major: 2 minor: 1 name: 'dubai-bakery' Cafe Section beacons: uuid: E2C56DB5-DFFB-48D2-B060-D0F5A71096E0 major: 2 minor: 2 name: 'dubai-cafe' Singapore (20 locations): Bakery Section beacons: uuid: E2C56DB5-DFFB-48D2-B060-D0F5A71096E0 major: 3 minor: 1 name: 'singapore-section' Cafe Section beacons: uuid: E2C56DB5-DFFB-48D2-B060-D0F5A71096E0 major: 3 minor: 2 name: 'singapore-cafe' ``` When building the pass via the PassEntry API, use the following JSON structure to define the beacons: ``` { "beacons": [ { "proximity_uuid": "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", "major": 1, "minor": 1, "message": "£10 off on cakes!", "name": "london-bakery" }, { "proximity_uuid": "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", "major": 1, "minor": 2, "message": "Buy one get one free juice", "name": "london-cafe" }, { "proximity_uuid": "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", "major": 2, "minor": 1, "message": "AED 20 off on cakes!", "name": "dubai-bakery" }, { "proximity_uuid": "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", "major": 2, "minor": 2, "message": "Try our pumpkin spice lattes!", "name": "dubai-cafe" }, { "proximity_uuid": "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", "major": 3, "minor": 1, "message": "$5 off on kuih lapis!", "name": "singapore-bakery" }, { "proximity_uuid": "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", "major": 3, "minor": 2, "message": "Buy one get one free teh tarik!", "name": "singapore-cafe" } ] } ``` # Pass Identifiers Source: https://docs.passentry.com/documentation/passes/identifiers/guide UUID vs extId - when to use each, and how to map passes to records in your own system. Every pass in PassEntry has a **UUID**, a system-generated identifier that permanently identifies the pass. Passes can also optionally carry an **extId** (external ID), a value you provide at pass creation to link the pass to a record in your own system. Both can be used to look up, update, or reference a pass via the API. The difference is in how they are assigned, whether they can be reused, and how you typically integrate them with your own records. ## UUID * Generated by PassEntry when a pass is created. * Globally unique and **immutable** for the lifetime of the pass. * Always present on every pass. * Returned in the API response when you create a pass, and available as `%{passData.uuid}` in dynamic values. ## extId * Provided by you at pass creation (optional). * Links a pass to a record in your own system, for example a CRM contact ID, a membership number, or an order reference. * **Unique across active passes** in your organisation. * **Freed only via nullification.** An extId becomes available for reuse only after the pass that owns it is nullified. See the [Pass Nullification guide](/documentation/passes/nullification/guide) for details. * Convenient as a lookup key: most pass endpoints accept either a UUID or an extId as the `pass_id` parameter. > [!NOTE] > > Nullification is irreversible, and nullified passes still count towards your billing quota. ## Two common patterns Which pattern fits depends on how you handle reissues. ### extId as the primary key You use your existing record ID (a CRM contact ID, membership number, or similar) as the pass extId. Your system stores only its own ID; the shared value is the link to PassEntry. Choose this if: * Your record is stable and maps 1:1 to a single active pass. * Nullifying a pass on reissue is acceptable, including the billing quota impact. * You want to look up passes by a meaningful business identifier in the API, webhooks, and notifications. To reissue: [nullify](/documentation/passes/nullification/guide) the existing pass, then create a new one with the same extId. ### UUID stored on your records You let PassEntry assign a UUID and store it on your record alongside your own ID. Choose this if: * A record may link to different passes over time and you want to avoid nullification on reissue. * Multiple active passes may coexist for the same record. * You want extId available as an optional tag, not a primary key. To reissue: create the new pass and update the stored UUID. Nullify the old pass if you want, or leave it active. ### Choosing between them Ask: when a pass needs to be replaced, is nullifying the existing one acceptable? If yes, using extId as the primary key keeps your integration simpler. If you need the old pass to stay active, or expect multiple passes per record, store the UUID on your records instead. If you are unsure which fits, [contact support](mailto:support@passentry.com). ## Where extId is used across PassEntry Even when extId is not your primary key, it is useful as a searchable tag on a pass: * **NFC message source** -- an NFC-enabled pass can emit its extId when tapped. See the [create pass](/api-reference/v1/passes/create-pass) reference for NFC source options. * **Barcode source** -- a pass's barcode can encode its extId. See the [localisation guide](/documentation/passes/localisation/guide) for configuration examples. * **Notification dynamic values** -- use `%{passData.extId}` in notification content alongside `%{passData.uuid}`. See the [notifications guide](/documentation/passes/notifications/guide). * **Batch pass creation** -- the `ext_id` column in the CSV sets the extId for each pass. See the [batch pass creation guide](/documentation/bulk-operations/batch-pass-creation/guide). * **Batch pass update** -- the `pass_id` column accepts either a UUID or an extId. See the [batch pass update guide](/documentation/bulk-operations/batch-pass-update/guide). * **Filtering** -- pass list endpoints support `extIdPrefix` for filtering by extId prefix. # Pass lifecycle: statuses, deactivation, and reactivation Source: https://docs.passentry.com/documentation/passes/lifecycle/guide Understand PassEntry pass statuses and what happens in Apple Wallet and Google Wallet when a pass is issued, deactivated, expired, or reactivated. A pass moves through several statuses during its life. This page explains what each status means, how to transition between them via the API, and how each transition is reflected in the user's wallet. For permanent, irreversible data removal, see [Pass Nullification](/documentation/passes/nullification/guide). ## Pass statuses | Status | Meaning | | - | - | | `issued` | The pass has been created but has not yet been added to a wallet. | | `active` | The pass is currently installed in a wallet. | | `removed` | The user has removed the pass from their wallet. The record remains in PassEntry. | | `deactivated` | The pass has been deactivated. It appears in the expired section of the user's wallet. | | `expired` | The pass has been expired via a scheduled `expiresAt`. | | `nullified` | The pass has been [nullified](/documentation/passes/nullification/guide). All dynamic data has been irreversibly removed. | ## Deactivating a pass You can deactivate a pass either immediately or on a schedule, from the Dashboard or via the API. ### From the Dashboard **Deactivate immediately** 1. Go to **Passes** and select the pass you want to deactivate. 2. In the **Pass management** section, click **Deactivate pass**. 3. Confirm in the dialog. The pass status changes to `deactivated` and the deactivation timestamp is recorded. **Schedule deactivation** 1. Go to **Passes** and click the edit icon on the pass you want to schedule. 2. In the **Pass expiry** section, set the expiry date, time, and timezone. 3. Save your changes. The pass is deactivated automatically when the expiry time is reached. **Bulk update via CSV** Use [Batch Pass Update](/documentation/bulk-operations/batch-pass-update/guide) to deactivate many passes at once. 1. Go to **Bulk Operations** and click **Update via CSV**. 2. Create a new batch, optionally name it, and select the pass template. 3. Download the sample CSV, then for each pass set: * `pass_id` — the pass UUID or external ID. * `pass_action` — `deactivate` to deactivate immediately, or `reactivate` to reactivate. * `expires_at` — an ISO 8601 UTC timestamp to schedule deactivation instead. 4. Upload the CSV and submit the batch. Track progress and resolve row-level errors from the batch detail view. See the [Batch Pass Update guide](/documentation/bulk-operations/batch-pass-update/guide) for the full column reference and error handling. > [!NOTE] > > Reactivating a pass from the Dashboard is only supported through Bulk Update via CSV (`pass_action=reactivate`). For a single pass, use the [API](#via-the-api). ### Via the API Use the [Update Pass](/api-reference/v1/passes/update-pass) endpoint to deactivate a pass, either immediately or on a schedule. To deactivate, set `passAction` to `deactivate`: ```json theme={null} { "passAction": "deactivate" } ``` To deactivate at a future time, set `expiresAt` to an ISO 8601 timestamp in UTC: ```json theme={null} { "pass": { "expiresAt": "2026-12-31T23:59:59Z" } } ``` `expiresAt` must be in the future. If your organisation is on a trial, it must also be earlier than your trial expiry. Scheduled deactivation is processed hourly, so the status change can lag the timestamp by up to an hour. ### What deactivation does * The pass status is set to `deactivated` and `deactivatedAt` is recorded. * The pass **cannot be added to a wallet**. Download and install requests are rejected. * When using PassEntry Validation, Scans and NFC taps are **refused**, with a refusal reason of `deactivated_pass`. The pass no longer works at entry points. * Wallet notifications are **not delivered**, on either platform. * The pass can still receive data updates through the API, and those updates are written to the wallet object. * The pass is **not** removed from the user's wallet. It moves to the expired or archived section. * The pass data and content **are unchanged** by default. To remove scanning data (Barcode/NFC) or pass content, include those changes in the same [Update Pass](/api-reference/v1/passes/update-pass) request that deactivates the pass. > [!NOTE] > > On Google Wallet, deactivation can take up to 24 hours to propagate to the user's device. The wallet object is updated immediately; the delay is in Google's device sync. ### Wallet behaviour **Apple Wallet** The pass is marked as voided and moves to the **expired passes** section of the wallet. It is not deleted; the user can still find it under expired passes. **Google Wallet** The pass state is set to `INACTIVE` and it moves to the inactive or archived section. It is **not** removed and remains accessible to the user. ## Reactivating a pass To reactivate a single pass, call [Update Pass](/api-reference/v1/passes/update-pass) with `passAction` set to `reactivate`. To reactivate many passes at once, use [Batch Pass Update](/documentation/bulk-operations/batch-pass-update/guide) with `pass_action=reactivate` in the CSV. ```json theme={null} { "passAction": "reactivate" } ``` The resulting status depends on where the pass was in its lifecycle: | State before reactivation | Resulting status | | - | - | | `deactivated`, and still in the holder's wallet | `active` | | `deactivated`, but never added to a wallet | `issued` | | `removed` | Unchanged. The holder must add the pass again. | **Apple Wallet** The pass remains in the **expired passes** section after reactivation. To move it back to the main passes view, the user must manually select **Unhide** on the pass in Apple Wallet. **Google Wallet** The pass state is set back to `ACTIVE` and returned to the main Wallet section. \ Changes can take up to 24 hours to reach the user's device. ## Lifecycle timestamps The pass record exposes the following timestamps, which are useful when reconciling state: * `expiresAt` - when the pass is scheduled to be deactivated. * `deactivatedAt` - when the pass was deactivated via an active request. * `removedAt` - when the user removed the pass from their wallet. * `nullifiedAt` - when the pass was nullified. ## Deactivation vs. nullification | | Deactivation | Nullification | | - | - | - | | Reversible | Yes (via `reactivate`) | **No** | | Pass data preserved | Yes | No. Dynamic fields, images, barcode, and NFC are cleared | | Appears in wallet | Yes, in the expired section | Yes, in the expired section with a generic "Deactivated" design | | `extId` reusable | No | Yes | | Counts towards billing | Yes | Yes | If you need to permanently remove passholder data (for example, for GDPR requests) or free up an `extId`, use [Pass Nullification](/documentation/passes/nullification/guide) instead. # Translating passes: localisation Source: https://docs.passentry.com/documentation/passes/localisation/guide Localise Apple Wallet and Google Wallet passes so labels and values automatically appear in the user's device language, and configure it with PassEntry. > [!WARNING] > > Localisation is available based on plan entitlements. > If your account does not currently have access, please contact PassEntry to enable this feature. ## Why localisation matters Localisation makes sure that the fields on your pass (including labels and values) appear in the **preferred language of your users' devices**. * Improves user experience * Builds trust and professionalism * Reduces confusion for international customers *** ## How devices decide which language to show Both **Apple Wallet** and **Google Wallet** automatically match the **device language** with the localisations you've defined: 1. The device checks its **system language settings**. 2. It looks for a **matching localisation** in your pass. 3. If a match exists → that language version of the pass is shown. This means you don't need to guess your user's preferred language. The wallet handles it for you. *** ## What if a device uses an unsupported language? Apple Wallet and Google Wallet handle fallbacks differently. | Platform | How the language is chosen when there's **no exact match** | | :- | :- | | **Apple Wallet** | Tries device preferred languages → if none match, searches for English localisation → if not present, defaults to first localisation alphabetically | | **Google Wallet** | Tries device locale → if none match, falls back to the template `defaultLanguage` | Example showing the mismatch: * Default language set on the template: Icelandic (`is`) * Localisations present: `fr`, `de`, `en` * Device language: `it` (Italian) **Apple Wallet:** displays English | **Google Wallet:** displays Icelandic > [!WARNING] > > To make behaviour consistent across Apple and Google and ensure a safe and predictable fallback, we recommend that you always **set the default language to English**. *** ## Supported locale options Both Apple and Google use **[BCP 47 language tags](https://en.wikipedia.org/wiki/IETF_language_tag)** to define locales. These tags combine a language code (e.g. `en` for English, `fr` for French) with optional region or script codes (e.g. `en-GB` for British English, `pt-BR` for Brazilian Portuguese). You can look up the language you need in the [Wikipedia list of IETF language tags](https://en.wikipedia.org/wiki/IETF_language_tag) and use the corresponding code when adding localisations to your pass. PassEntry supports a wide range of locales used by Apple and Google. You can include multiple localisations per pass template. Here's the current list of supported locales: ```javascript theme={null} SUPPORTED_LOCALES = [ "af", "am", "ar", "arn", "ary", "as", "az", "ba", "be", "bg", "bn", "bo", "br", "bs", "ca", "ckb", "co", "cs", "cy", "da", "de", "dsb", "dv", "el", "en", "es", "et", "eu", "fa", "fi", "fil", "fo", "fr", "fy", "ga", "gd", "gil", "gl", "gsw", "gu", "ha", "he", "hi", "hr", "hsb", "hu", "hy", "id", "ig", "ii", "is", "it", "iu", "ja", "ka", "kk", "kl", "km", "kn", "ko", "kok", "ku", "ky", "lb", "lo", "lt", "lv", "mi", "mk", "ml", "mn", "moh", "mr", "ms", "mt", "my", "nb", "ne", "nl", "nn", "no", "oc", "or", "pap", "pa", "pl", "prs", "ps", "pt", "pt-BR", "pt-PT", "quc", "qu", "rm", "ro", "ru", "rw", "sa", "sah", "se", "si", "sk", "sl", "sma", "smj", "smn", "sms", "sq", "sr", "st", "sv", "sw", "syc", "ta", "te", "tg", "th", "tk", "tn", "tr", "tt", "tzm", "ug", "uk", "ur", "uz", "vi", "wo", "xh", "yo", "zh", "zh-Hans", "zh-Hant", "zu" ] ``` *** ## Example: localised supermarket membership pass Let's walk through a simple case study. Imagine a supermarket in France that issues a digital loyalty card. They want the pass to appear in English by default for tourists, while also providing a French translation for their local customers. Below is a minimal pass template definition with localisation applied: ```json theme={null} { "passTemplate": { "name": "PassEntry Supermarket", "templateType": "generic", "defaultLanguage": "en", "centralTitle": "PassEntry Supermarket", "notificationHeader": "PassEntry Supermarket", "description": "PassEntry Supermarket", "colors": { "background": "#dbdbdb", "label": "#000000", "text": "#000000" }, "images": { "banner": "https://example.com/images/banner.jpg", "logo": "https://example.com/images/logo.jpg", "icon": "https://example.com/images/icon.jpg" }, "fields": { "header": { "one": { "id": "points", "label": "Points", "defaultValue": "0", "localisation": { "fr": { "label": "Points", "defaultValue": "0" } } } }, "central": { "one": { "id": "membershipTier", "label": "Tier", "defaultValue": "Silver", "localisation": { "fr": { "label": "Niveau", "defaultValue": "Argent" } } } }, "details": { "one": { "id": "discount", "label": "Discount", "defaultValue": "15%", "localisation": { "fr": { "label": "Réduction", "defaultValue": "15%" } } }, "two": { "id": "memberId", "label": "Member ID", "localisation": { "fr": { "label": "Identifiant membre" } } } } }, "barcode": { "enabled": true, "type": "qr", "source": "extId" } } } ``` ## Pass value localisation In addition to localising **labels** and **default values** at the template level, you can also localise **field values** directly when creating or updating a pass.\ This is especially useful for dynamic, user-specific data (like membership tier, discount, or status), so the right translation appears without requiring multiple template versions. Example use case: * A loyalty programme upgrades certain users from **Level 1** to **Level 2**. * By localising the pass value directly, you can update each user’s pass once and rely on localisation to handle all supported languages. ### Example: localised membership tier value ```json theme={null} { "pass": { "membershipTier": { "value": "Level 2", "localisation": { "fr": { "value": "Niveau 2" }, "es": { "value": "Nivel 2" }, "de": { "value": "Stufe 2" } } } } } ``` The top-level `value` and each `localisation..value` accept a string, integer, float, or `null`. Use a number when the field represents a numeric quantity (e.g. points, tier number, price) so different locales can render it in the appropriate script or format. ```json theme={null} { "pass": { "points": { "value": 1250, "localisation": { "ar": { "value": "١٢٥٠" }, "fr": { "value": 1250 } } } } } ``` *** ### Notes * **`defaultLanguage`:** Always set to `en` for a consistent fallback on both Apple and Google Wallet. * **Localisation scope:** Added for labels and static default values where appropriate. Avoid defaults for dynamic customer data (e.g. name, email, member ID). * **How `defaultLanguage` works:** * If `defaultLanguage` is set to `en`, then the parent-level `"label": "Tier", "defaultValue": "Silver"` are already in English. * Do **not** repeat English under the `localisation` object – it's redundant. * Only add translations for other languages (e.g. `fr`, `de`). * **Changing the default language later:** If you decide to make French your default language, you must: 1. Update the parent-level label and default value to French. 2. Remove `fr` from the `localisation` object. 3. (Optionally) add `en` as a localisation if you still want English support. * **Always include all localisation entries when updating a pass template or pass:**\ If you previously set up French, Spanish, and German and then send an update with only German, PassEntry **removes** the French and Spanish translations. * **Localised default values do not propagate to existing passes on their own:**\ Changing a localised `defaultValue` on a template applies to newly created passes only. To push it to existing passes, set `overrideFieldValue: true` on that field in the same update request. See [Field default values do not propagate by default](/api-reference/v1/pass-templates/guide#field-default-values-do-not-propagate-by-default). ### Example **Default language = English** ```json theme={null} { "label": "Tier", "defaultValue": "Silver", "localisation": { "fr": { "label": "Niveau", "defaultValue": "Argent" } } } ``` **Default language switched to French** ```json theme={null} { "label": "Niveau", "defaultValue": "Argent", "localisation": { "en": { "label": "Tier", "defaultValue": "Silver" } } } ``` > [!TIP] > > To test localisation on your phone, install your pass, change the language preferences in your device settings and then return to the Wallet app. # Notifications Source: https://docs.passentry.com/documentation/passes/notifications/guide Learn how to send wallet-native notifications to Apple Wallet and Google Wallet passes, including platform limits, best practices and how to include links and dynamic data in notifications. **Wallet notifications** let you reach customers at the right moment – directly through their Apple Wallet or Google Wallet pass. Use notifications to share important updates like event changes, expiring memberships, or account activity without relying on email or SMS. When a notification is sent, it appears on the device lock screen and is also stored on the back of the pass as the **Latest Update**, so users can always see the most recent message. > [!WARNING] > > Wallet notifications are delivered on a **best-effort basis**. Neither Apple nor Google guarantee immediate or reliable delivery of lock screen notifications. Some delay between sending and delivery is expected. See [Troubleshooting](#troubleshooting) for platform-specific details. ### Best practices Apple Wallet and Google Wallet apply **different limits and delivery behaviour** for notifications. Keep the following platform-specific constraints in mind when designing your notification strategy. **Google** Currently limits their push notifications to **3 per day** per pass, if you go over this quota, the passholder will not receive a push notification on their lock screen, but it will update the "Latest Update" on the back of the pass. **Apple** Limits their push notifications to **10 per day** and may sometimes not show push notifications if sent within a very short interval (seconds between notifications) *** As a general rule, we recommend limiting notifications to **no more than 3 per day per pass** to avoid rate limits and reduce notification fatigue. ### Sending Notifications If sending notifications to an individual pass, use our [Send Notification](/api-reference/v1/passes/send-notification) endpoint. If you wish to send notification alongside a pass update, you can do so using our [Update Pass](/api-reference/v1/passes/update-pass) or [Update loyalty balance](/api-reference/v1/loyalty/update-loyalty-balance) endpoints ### Including a link in the notification When including a link in the notification, the link will be opened when the user taps the notification. However, the format of a clickable link is different for each platform. We offer a simple solution to generate a clickable link for both platforms. **Apple** Include a valid URL in your notification. "Visit [https://www.passentry.com](https://www.passentry.com) for more!" or "Visit [passentry.com](https://www.passentry.com) for more!" In order to make the link clickable, the user must press and hold the notification until the link is highlighted. Apple does not support HTML tags in notifications, but autodetects the link and makes it clickable. **Google** Links must be formatted as HTML `` tags. `Visit https://passentry.com for more!` The text is the text that will be visible on the notification, and can be different from the URL text. `Visit this link for more!` *** **Using PassEntry's link generator** As the device type is not always known, we offer a simple solution to generate a clickable link for both platforms. To create a link, enclose the link text in brackets (e.g., \[this link]) and then follow it immediately with the URL in parentheses (e.g., ([https://passentry.com](https://passentry.com))). `Visit [this link](https://passentry.com) for more!` We will automatically generate the correct link for the platform. > [!WARNING] > > Make sure to always send a test message to verify that the link is clickable and working as expected. You must include the link text \[this link] in the notification, and the URL in parentheses (e.g., ([https://passentry.com](https://passentry.com))). **The URL must always begin with http\:// or https\://** Link text is ignored on Apple, but will appear on Google. It is always required, if you don't want to show any custom text, the link text should be the same as the URL text. **Example:** The following notification `Visit [this link](https://passentry.com) for more! ` will be displayed as: *Apple:* Visit [https://passentry.com](https://passentry.com) for more! *Google:* Visit [this link](https://passentry.com) for more! ### Dynamically inserting variables in the notification You can also include variables in the notification using the `%{passData.variable_name}` syntax. We currently support the following variables: | Variable | Description | | - | - | | `%{passData.uuid}` | The UUID of the pass | | `%{passData.extId}` | The external ID of the pass - shown as an empty string if the pass does not have an external ID | Example: `Visit https://passentry.com/membership-tier/%{passData.extId} for more!` Will be displayed as: `Visit https://passentry.com/membership-tier/1234567890 for more!` The variable will be replaced with the value of the variable when the notification is sent. ## Troubleshooting ### Apple If you are having issues receiving push notifications on your iOS device, please check the following: * Ensure that you have enabled push notifications for the specific pass by toggling `Automatic Updates` and `Allow Notifications`. These options are enabled by default on the Wallet. ![Apple Wallet Notification Permissions](https://mintcdn.com/passentry-0/Pme8e7kS-lNx7reF/documentation/passes/notifications/notification-permissions.jpeg?fit=max\&auto=format\&n=Pme8e7kS-lNx7reF\&q=85\&s=ad9118de620377e5e5c65218031cd326) * Notifications are usually displayed in real time, however, when sending a large number of notifications to a single pass, Apple will sometimes rate limit the notifications, which will result in the notification delivery being delayed. Although quite common during development or testing, this will not typically happen in live environments, as most passes will not usually be sending more than 10 notifications per day. * For geofence notifications, please ensure that Location Services are enabled for the Apple Wallet. `Settings -> Privacy & Security -> Location Services -> Wallet` ![Apple Wallet Location Permissions](https://mintcdn.com/passentry-0/Pme8e7kS-lNx7reF/documentation/passes/notifications/location-permissions.jpeg?fit=max\&auto=format\&n=Pme8e7kS-lNx7reF\&q=85\&s=a0a76c19d3863ee636737680b4be64f0) * To force an update to the pass, you can drag and pull down on the pass from the back of the pass. This will force any new notifications to be displayed, regardless of notification permissions or rate limits. ![Apple Wallet Force Update](https://mintcdn.com/passentry-0/Pme8e7kS-lNx7reF/documentation/passes/notifications/forcing-update.gif?s=f26232ee1a08f4c3d7095c128a8fa2f6) ### Google If you are having issues receiving push notifications on your Android device, please check the following: * Ensure notification permissions are enabled for the Google Wallet. This is enabled by default. ![Google Wallet notification permissions](https://mintcdn.com/passentry-0/Pme8e7kS-lNx7reF/documentation/passes/notifications/google-notification-permissions.png?fit=max\&auto=format\&n=Pme8e7kS-lNx7reF\&q=85\&s=4a1273c4a4f24c3cd585422d975b2833) * Google wallet notifications are not displayed in real time, as the Google Wallet has a less frequent update cycle than Apple Wallet. It usually takes between 10 seconds and 2 minutes for it to show up as a lock screen notification. To force an update, you can close and reopen the Google Wallet app. * Google does not include the link text in the notification, it instead displays a generic "New Message" text, alongside the pass template logo icon. ![Google Wallet notification preview](https://mintcdn.com/passentry-0/Pme8e7kS-lNx7reF/documentation/passes/notifications/notification-preview.png?fit=max\&auto=format\&n=Pme8e7kS-lNx7reF\&q=85\&s=3fa973e921f19e069c181661445ce932) * Google rate limits notifications to 3 per day per pass, if you go over this quota, the passholder will not receive a push notification on their lock screen, but it will display an "Important Message" update from within the wallet, as well as updating the `Latest Update` field on the pass. * If you're still unable to receive Google Wallet notifications, your Google Wallet may be set to a different Google account than the one with the pass that you are trying to send notifications to. This is common for individuals with multiple emails on their device, such as work and personal accounts. To switch accounts you can tap the profile icon in the top right corner of the Google Wallet app, or swipe through the account icons vertically to switch between them. ![Google Wallet account switching](https://mintcdn.com/passentry-0/Pme8e7kS-lNx7reF/documentation/passes/notifications/account-switching.gif?s=2a100fae020d34ea3accf34e393f084c) # Pass nullification: GDPR-compliant data removal Source: https://docs.passentry.com/documentation/passes/nullification/guide Permanently remove passholder data from a PassEntry pass while keeping a tombstone record for billing, and reuse the External ID afterwards. Pass nullification allows you to irreversibly remove all dynamic data from a pass while preserving a tombstone record for billing and auditability. This is useful for GDPR-compliant data removal, deprovisioning users, or freeing up an External ID for reuse. > [!WARNING] > > Nullification is **irreversible**. Once a pass is nullified, its data cannot be recovered. Make sure this is the intended action before calling the endpoint. ## What happens when a pass is nullified When you call the [Nullify Pass](/api-reference/v1/passes/nullify-pass) endpoint, the following changes are applied: * **Status** is set to `nullified` * **Dynamic fields** (text fields, barcode, NFC, localisation, latest message) are cleared * **Images** (logo, banner, thumbnail) are purged from the pass * **Pass design** is replaced with a generic "Deactivated" appearance in grey * **Wallet state** -- the pass moves to the expired section of the wallet and cannot be restored The following are **preserved**: * Pass UUID and serial number * External ID (`extId`) * Creation and nullification timestamps * The pass record itself (for billing quota and audit) > [!NOTE] > > Nullified data is removed from active systems immediately. Encrypted database backups are retained for up to 35 days, and logs referencing the pass data for up to 365 days, after which all remaining copies are permanently deleted. ### Wallet appearance **Apple** The pass appears in the expired passes section with grey colours, empty fields, and a "Deactivated" label. The pass is marked as voided. **Google** The pass shows a "Deactivated" header with a grey background. All text fields, barcode, and pass-level images are removed. The pass state is set to `INACTIVE`. Note that images set at the **template level** (e.g. logo, hero image) are managed by Google and cannot be removed by nullification. These will remain visible on the pass. ## API behaviour after nullification Once a pass is nullified, it is excluded from most API endpoints. Requests to the following endpoints using a nullified pass's UUID or `extId` will return `404 Not Found`: * [Get Pass](/api-reference/v1/passes/show-pass) * [Update Pass](/api-reference/v1/passes/update-pass) * [Send Notification](/api-reference/v1/passes/send-notification) Attempts to download the pass via its `downloadUrl` will also fail with a pass not found error. Nullified passes are also excluded from [List Passes](/api-reference/v1/passes/list-passes) results by default. To include them, pass `includeNullified=true` as a query parameter, or filter by `status=nullified`. The [Nullify Pass](/api-reference/v1/passes/nullify-pass) endpoint remains accessible for already-nullified passes (see [Idempotency](#idempotency)). ## External ID reuse After nullification, the pass's `extId` is freed for reuse. You can create a new pass with the same `extId` within the same organisation. This is useful when: * A user wants to switch pass type (e.g. loyalty to event ticket) but their `extId` is tied to a single pass * A user loses their device and needs a fresh pass reissued under the same identifier * A pass needs to be deprovisioned and later re-provisioned for the same user Multiple nullified passes with the same `extId` can coexist -- uniqueness is only enforced across non-nullified passes. ## Billing Nullified passes **still count towards your billing quota**. Nullification removes data, not the record. If you need to view nullified passes, use the `status` filter on the [List Passes](/api-reference/v1/passes/list-passes) endpoint with `status=nullified`. ## Idempotency The nullify endpoint is idempotent. Calling it on an already-nullified pass returns `200` with no further changes. ## Nullify vs deactivate Both actions make a pass appear inactive in the wallet, but they serve different purposes. Use `deactivate` when you may need to bring the pass back; use nullify when the pass and its data must be permanently removed. | | Deactivate | Nullify | | - | - | - | | **Endpoint** | [Update Pass](/api-reference/v1/passes/update-pass) with `passAction: "deactivate"` (reverse with `"reactivate"`) | [Nullify Pass](/api-reference/v1/passes/nullify-pass) | | **Reversible?** | Yes -- call again with `passAction: "reactivate"` | No -- permanent | | **Pass data** | Preserved (fields, barcode, NFC, images) | All dynamic data cleared | | **Wallet appearance** | Moved to expired section, existing design retained | Generic grey "Deactivated" design | | **API access** | Pass remains accessible on all endpoints | Excluded from most endpoints (returns `404`) | | **`extId` reuse** | Not freed | Freed for reuse within the organisation | | **Typical use case** | Temporarily suspend a pass (e.g. paused subscription, expired offer you may re-enable) | GDPR data removal, deprovisioning, permanent revocation | Both actions leave the pass record in place and it continues to count towards your billing quota. # Pass Binding Source: https://docs.passentry.com/documentation/passes/pass-binding/guide Restrict digital passes to a specific wallet account, preventing sharing and unauthorized use on both Apple Wallet and Google Wallet. > [!WARNING] > > This feature is in **open beta** and available via custom plan subscription to get access - let us know if you encounter any issues or have feedback. Pass binding ties a digital pass to a specific wallet account, ensuring that only the intended recipient can use it. This is useful for preventing pass sharing, securing NFC-based access, and ensuring passes are only transactable by the verified holder. ## How it works Binding works differently on each platform, but the setup is the same - you provide a `binding` object when creating a pass via the API. **Google Wallet** Google Wallet uses email-based restrictions. When a pass with binding is downloaded on an Android device, PassEntry generates a SHA-256 hash of the provided email and stores it in the pass. Only the Google account matching that email can save the pass. Any other user who tries to save the pass will see an error message. ![Google Wallet pass restricted error](https://mintcdn.com/passentry-0/bR_OrQGn0BA6y-X_/documentation/passes/pass-binding/google-pass-restricted.jpg?fit=max\&auto=format\&n=bR_OrQGn0BA6y-X_\&q=85\&s=55b7a20127e6927b5775df1aab857ae8) **Apple Wallet** Apple Wallet uses FIDO hardware key binding. When a pass with binding is downloaded on an iOS device, the user is prompted to authenticate using Face ID or Touch ID. This creates a hardware-backed cryptographic key on the device's secure element, which is tied to the user's iCloud account. Apple binding is particularly beneficial for NFC passes. Once bound, the NFC functionality of the pass is restricted to only work for the authenticated user, preventing anyone else from using the pass at NFC readers. Supported on iOS 16.4+ and watchOS 9.4+ only. ![Apple Wallet Face ID authentication](https://mintcdn.com/passentry-0/bR_OrQGn0BA6y-X_/documentation/passes/pass-binding/apple-face-id.png?fit=max\&auto=format\&n=bR_OrQGn0BA6y-X_\&q=85\&s=838fbc05603446143035011b1aa483b5) ## Prerequisites Before using account binding, ensure the following: * Your organisation has the **pass binding** feature enabled. [Contact PassEntry](mailto:support@passentry.com) if you need access. * You have a `learnMoreUrl` ready - this is a page on your website where pass holders can get help if their pass enters an invalid state (e.g., a support or FAQ page). * Your passes **do not rely on barcodes**. Barcodes are prohibited on passes with binding enabled. If your use case requires visual identification, consider using NFC instead. > [!NOTE] > > Barcodes are prohibited on all passes (both Apple and Google) when binding is enabled. This is enforced at pass creation time and also during pass updates - the API will reject requests that include both a barcode and binding. ## Creating a pass with binding Add the `binding` object to your [pass creation request](/api-reference/v1/passes/create-pass). Binding can only be set at creation time and cannot be added, changed, or removed later. ```json Create pass with binding theme={null} POST /api/v1/passes?passTemplate=YOUR_TEMPLATE_UUID { "pass": { "fullName": { "value": "Jane Smith" }, "nfc": { "enabled": true } }, "binding": { "email": "user@gmail.com", "learnMoreUrl": "https://support.example.com/pass-help" } } ``` | Parameter | Required | Description | | - | - | - | | `binding.email` | Yes | Email address of the pass holder. For Google Wallet, this must match the user's Google account. | | `binding.learnMoreUrl` | Yes | HTTPS URL to a page on your website where the pass holder can get help resolving issues. Must be 255 characters or less. | The response will include the binding status: ```json Response (after creation, before download) theme={null} { "data": { "id": "33b680b7a5460e6cd4646dfd2c1a8c33a6fbfa5c", "type": "pass", "attributes": { "downloadUrl": "https://download.passentry.com?pass=33b680b7...", "status": "issued", "binding": { "platform": null, "bound": false, "bindingEmail": "u***@gmail.com", "learnMoreUrl": "https://support.example.com/pass-help" } } } } ``` At this stage, the pass has a binding marker but is not yet bound to a platform. The `platform` and `bound` fields are set when the pass is downloaded. ## Binding lifecycle A pass with binding goes through the following states: | State | `platform` | `bound` | Description | | - | - | - | - | | Created | `null` | `false` | Pass created with binding, not yet downloaded | | Google bound | `"google"` | `true` | Pass downloaded on Android, restricted to the specified Google account | | Apple pending | `"apple"` | `false` | Apple binding initiated, waiting for device authentication | | Apple bound | `"apple"` | `true` | Device authenticated via Face ID/Touch ID, binding complete | ## Platform lock Once a pass is downloaded and bound to a platform, it cannot be downloaded on the other platform. For example, if a pass is bound to Apple Wallet, attempting to download it on an Android device will return an error. This also applies to the PassEntry download page - the page will detect the platform mismatch and display an appropriate message to the user. ## What the user sees **Google Wallet** When a user with the correct Google account opens the download link, the pass is saved to their wallet normally. If a different user tries to save the same pass, they will see an error: ![Google Wallet pass restricted error](https://mintcdn.com/passentry-0/bR_OrQGn0BA6y-X_/documentation/passes/pass-binding/google-pass-restricted.jpg?fit=max\&auto=format\&n=bR_OrQGn0BA6y-X_\&q=85\&s=55b7a20127e6927b5775df1aab857ae8) **Apple Wallet** When an iOS user opens the download link for a bound pass, they are guided through the FIDO authentication flow: 1. The user sees a prompt explaining that the issuer requires enhanced security 2. The user authenticates with Face ID or Touch ID 3. The pass is added to their wallet with binding active ![Apple Wallet Face ID authentication](https://mintcdn.com/passentry-0/bR_OrQGn0BA6y-X_/documentation/passes/pass-binding/apple-face-id.png?fit=max\&auto=format\&n=bR_OrQGn0BA6y-X_\&q=85\&s=838fbc05603446143035011b1aa483b5) If the pass enters an invalid state, users may see one of the following states on their pass: 1. **Activating** — The pass is being activated. It will become usable once binding is successful. 2. **Pass Disabled (Sign in required)** — The user is signed out of iCloud and needs to sign back in with the Apple ID they used when adding the pass. 3. **Pass Disabled (Different Apple ID)** — The pass is associated with a different Apple ID. The user is directed to contact the issuer via the `learnMoreUrl`. ![Apple pass activating state](https://mintcdn.com/passentry-0/bR_OrQGn0BA6y-X_/documentation/passes/pass-binding/apple-activating.png?fit=max\&auto=format\&n=bR_OrQGn0BA6y-X_\&q=85\&s=0e8e20c9d209fb84d5992ba9ea3e7004) ![Apple pass disabled - sign in required](https://mintcdn.com/passentry-0/bR_OrQGn0BA6y-X_/documentation/passes/pass-binding/apple-sign-in.png?fit=max\&auto=format\&n=bR_OrQGn0BA6y-X_\&q=85\&s=33341e5e12e9ac74e6fd363deb555d49) ![Apple pass disabled - different Apple ID](https://mintcdn.com/passentry-0/bR_OrQGn0BA6y-X_/documentation/passes/pass-binding/apple-pass-disabled.png?fit=max\&auto=format\&n=bR_OrQGn0BA6y-X_\&q=85\&s=f66b3fef659a1dcfa1e9b5f20791c775) ## Important limitations > [!WARNING] > > Binding **cannot be updated or removed** after the pass is created. To change binding details, you must create a new pass. Plan your binding configuration carefully before issuing passes. * **No barcodes**: Barcodes are prohibited on all passes with binding enabled. The API will reject creation requests that include both binding and a barcode. * **No retroactive binding**: You cannot add binding to an existing pass via the update endpoint. Binding must be set at pass creation time. * **Platform lock**: Once bound to Apple or Google, the pass cannot be downloaded on the other platform. * **Apple device requirements**: Apple binding requires iOS 16.4+ or watchOS 9.4+ with Face ID, Touch ID, or a passcode configured. * **Email handling**: For Google-bound passes, the email is stored encrypted and masked in API responses. For Apple-bound passes, the email is purged after the binding is initiated (it is not needed for FIDO authentication). ## Checking binding status Binding data is automatically included in single pass responses (create, show, update). For the list endpoint, add `?includeBinding=true` to include binding data. ```bash theme={null} GET /api/v1/passes/PASS_UUID ``` ```json Google-bound pass response theme={null} { "binding": { "platform": "google", "bound": true, "bindingEmail": "u***@gmail.com", "learnMoreUrl": "https://support.example.com/pass-help" } } ``` ```json Apple-bound pass response theme={null} { "binding": { "platform": "apple", "bound": true, "bindingEmail": null, "learnMoreUrl": "https://support.example.com/pass-help" } } ``` Note that `bindingEmail` is `null` for Apple-bound passes because the email is purged after binding initiation. ## Troubleshooting | Issue | Cause | Solution | | - | - | - | | `400` when creating pass with binding and barcode | Barcodes are prohibited with binding | Remove the barcode from the request | | Google user sees "pass is intended for another account" | Email doesn't match the user's Google account | Ensure the `binding.email` matches the intended recipient's Google account | | Apple pass shows "Pass Disabled" | User signed out of iCloud or moved pass to a device with a different Apple ID | Direct user to sign back into their iCloud account. The `learnMoreUrl` is shown on the pass for support. | | Pass cannot be downloaded on the other platform | Platform lock is active | This is expected. Create a new pass if the user needs it on a different platform. | | `422` when trying to add binding via update | Binding can only be set at creation | Create a new pass with the binding object | ## Related * [Create Pass API](/api-reference/v1/passes/create-pass) # PassEntry FAQs: API keys, NFC, templates, and limits Source: https://docs.passentry.com/faq/guide Answers to common PassEntry questions on API keys, NFC approval, team roles, app linking, field line breaks, trial passes, and resource limits. ### Where can I get an API key? The API key is available in the [Developer Tools](https://app.passentry.com/developer-tools) section of the PassEntry dashboard. ![](https://mintcdn.com/passentry-0/Pme8e7kS-lNx7reF/faq/api-key.png?fit=max\&auto=format\&n=Pme8e7kS-lNx7reF\&q=85\&s=91592f85efa755044e2c83e7b33f0416) ### How do I issue production NFC passes? To issue NFC passes for live production usage, PassEntry must approve your use case. You provide proof of your use case, company information, and fill out a form to request approval. PassEntry may ask you for additional information. The process typically takes a few days. Apple and Google grant final approval, and PassEntry facilitates that step. This process keeps PassEntry compliant with Apple and Google's terms of service. ### Can I create a pass with both NFC and barcode? Yes, you can create a pass with both NFC and barcode. On Apple, the barcode appears in the Pass Details section. On Google, a "Show code" button appears under the pass section. See below for exceptions. ### Why do I have to choose to enable Google NFC when creating a loyalty pass? Loyalty templates have a setting called "Enable Google NFC". When you enable it and set a barcode value with no NFC value, the pass presents that value as both a barcode and an NFC value. When you set an NFC value with no barcode value, the pass displays only the NFC value. When you disable the setting, the pass displays only the barcode. ### How do I invite team members and change their roles? Organisation admins invite, remove, and change roles for team members from the [Account Settings page](https://app.passentry.com/account-settings) in the dashboard. From that page, open the **Team** section and use **Send invite** to email an invitation. To change or remove an existing member, open the three-dot menu on their row and choose **Change Role** or **Delete**. PassEntry supports three roles: * **Organisation Admin:** manages organisation settings, users, and access across every entity. * **Entity Admin:** manages settings and users for a single entity. * **Member:** view-only access to assigned entities and features. ### How do I add a line break inside a pass field value? Use `\n` in the field value to insert a line break. For example, `"London\nUK"` renders as `London` on the first line and `UK` on the second. When you set field values in a CSV for [batch pass creation](/documentation/bulk-operations/batch-pass-creation/guide) or [batch pass update](/documentation/bulk-operations/batch-pass-update/guide), wrap the value in double quotes so the CSV parser preserves the `\n` escape. ### How do I change the app or website that a pass links to? The **App Link** feature on the pass template controls the link that appears on a pass. Apple Wallet and Google Wallet label this link "View Offers", "Open Pass", or similar depending on the template. To update it in the dashboard, open the pass template on the [Pass Templates page](https://app.passentry.com/pass-templates) and update the links for each platform: * **Apple:** open the **Apple** tab, go to **Features**, and set one or more App Store URLs. Apple Wallet displays the first entry available in the passholder's country. * **Google:** open the **Google** tab, go to **Features**, and set either a web URL that opens in a browser or a Play Store link. You can also set custom display text (default is `Open Pass`). To update it via the API, set the `passTemplate.appLinking` object on [Create Pass Template](/api-reference/v1/pass-templates/create-pass-template) or [Update Pass Template](/api-reference/v1/pass-templates/update-pass-template). Changes to the template apply to passes issued from it. ### How do I delete a pass template? You can delete pass templates directly from the [Pass Templates page](https://app.passentry.com/pass-templates) — open the "…" menu on any template card and pick **Delete**. Any associated passes will be **nullified** (see the [Pass Nullification guide](/documentation/passes/nullification/guide)), and this action can't be undone. Only published templates count against your template limit, so removing old published templates frees up quota. ### How do I delete my PassEntry account? Account deletion is not self-service. To close your account or cancel your subscription, contact the PassEntry team at [support@passentry.com](mailto:support@passentry.com) and they will handle the request. ### How do I remove the "Trial Pass — Not for Distribution" banner from my passes? Passes issued on a free trial carry a "Trial Pass — Not for Distribution" banner overlaid on the pass artwork. You can't remove the banner while your organisation is on the trial plan. Upgrade to a paid plan to remove it. > [!WARNING] > > Trial passes are for internal testing only. Do not distribute them to end customers. When your organisation goes live on a paid plan, PassEntry deactivates existing trial passes because they were issued under trial wallet credentials that get replaced during the go-live setup. Re-issue any passes you want to keep after the upgrade. To upgrade, contact [sales@passentry.com](mailto:sales@passentry.com) or use the upgrade prompt in the dashboard. ### Can I deliver a pass to someone who doesn't use Apple Wallet or Google Wallet? PassEntry passes are Apple Wallet and Google Wallet objects. There is no built-in PDF pass or standalone image export that you can use in place of a wallet pass. For passholders on a desktop or any device that is not an iPhone or Android phone, the [hosted download page](/documentation/passes/downloading-passes/guide) shows a QR code that links back to the same `downloadUrl`, so the passholder can scan it with a phone to add the pass to their wallet. If you only need a scannable code and not a wallet object (for example, at a physical till or turnstile), read the pass barcode value from the API response and render it yourself. Each pass exposes a barcode value (`qr`, `code128`, `aztec`, or `pdf417`) that your own system can generate as an image and print, email, or embed in a PDF you produce. Your system then validates that code using the [Third-Party Validation](/documentation/guides/validation-options/guide) flow. ### Where can I find information about security and compliance (SOC 2, GDPR)? PassEntry publishes security and compliance information, including certifications, audit reports, and data processing details, on the [PassEntry Trust Center](https://trust.passentry.com/). If you have questions the trust center does not answer, contact [support@passentry.com](mailto:support@passentry.com). For product features that support compliance workflows, see: * [Pass Nullification](/documentation/passes/nullification/guide) for GDPR-style irreversible data removal. * [Pass Binding](/documentation/passes/pass-binding/guide) for FIDO-backed authentication on NFC passes. ### What is a group tag and how do I use one? A **group tag** is a free-form label you attach to a pass so you can segment pass holders later. A pass can carry one or more group tags. Tags are set at pass creation or update time, not defined centrally on the pass template. You can apply group tags in several ways: * **Create Pass API:** set `groupTag` (single) or `groupTags` (array) on [Create Pass](/api-reference/v1/passes/create-pass) or [Update Pass](/api-reference/v1/passes/update-pass). * **Batch pass creation:** use the `group_tags` CSV column with comma-separated tags. See [Batch Pass Creation](/documentation/bulk-operations/batch-pass-creation/guide). * **Custom Pass Issuance Form URL:** append `?groupTag=example-tag` or `?groupTags=tag1,tag2` to the form URL, or map a form field to a group tag. See [Custom Pass Issuance Forms](/documentation/guides/custom-download-forms/guide). Once tags are applied you can filter by them when listing passes (`groupTag` / `groupTags[]` query parameters) and when sending [Batch Notifications](/documentation/bulk-operations/batch-notifications/guide). In batch notifications you can also combine a group tag segment with a pass template segment to target passes that match both. ### What are soft and hard limits? PassEntry uses two types of resource limits: **Soft limits:** Flexible thresholds that you can exceed. PassEntry notifies you when this happens, but your service continues running. Upgrade your plan when you consistently exceed soft limits. **Hard limits:** Strict thresholds that you cannot exceed. Once you reach a hard limit, upgrade your plan to create more resources. **Note:** All limits are hard limits unless specified as soft limits. # We value your feedback Source: https://docs.passentry.com/feedback/guide We are always looking for ways to improve our product and services. If you have any feedback, please let us know. ## How to give feedback 1. **Fill out our anonymous [feedback form](https://wpxnht5lj5w.typeform.com/to/BwU415Nj)** - It only takes a few minutes! 2. **Email us at [support@passentry.com](mailto:support@passentry.com)** - We're always happy to hear from you. # Getting Started with PassEntry Source: https://docs.passentry.com/index Learn how to create templates, issue digital passes and add them to Apple Wallet & Google Wallet using the PassEntry dashboard. ## Overview PassEntry lets you create, issue, and manage digital passes for Apple Wallet and Google Wallet using either a dashboard or an API. > [!NOTE] > > Following this guide, most teams can complete the setup in **under 15 minutes** using the PassEntry dashboard. This quickstart guide shows how to: * [Create a pass template](#step-1-create-a-pass-template) * [Issue a digital pass](#step-2-issue-a-pass) * [Add a pass to a mobile wallet](#step-3-add-the-pass-to-a-wallet) * [Update an existing pass](#step-4-updating-an-existing-pass) * [Send a notification](#step-5-sending-notifications-to-pass-holders) *** ## What is PassEntry? PassEntry is an API-first platform for issuing and managing **digital wallet passes**, including: * Event tickets * Membership cards * Loyalty passes * Transit passes and more. Passes can be added to **Apple Wallet** and **Google Wallet**, updated in real time and distributed via any of your existing communication channels such as email, SMS, QR codes or automated workflows. *** ## Prerequisites Before you start, make sure you have: * A PassEntry account (you can start a **free 14-day trial** at [https://app.passentry.com/signup](https://app.passentry.com/signup)) * Sample brand assets if you want to customise the appearance of your passes *** ## Core concepts These concepts are used throughout the documentation: * **Pass template**. A reusable definition of a pass. Defines layout, branding, field labels, and static values that are the same for every issued pass. * **Pass**. A single issued instance of a template. Inherits design and static values from the template and adds dynamic, user-specific data (for example: name, membership ID, seat). * **Pass download link**. A secure, pass-specific URL that lets a user add a pass to Apple Wallet or Google Wallet. *** ## Step 1: Create a pass template A pass template defines the structure and appearance of your passes. ### Create a template in the dashboard ![](https://mintcdn.com/passentry-0/m-Fi8rWVvgP3bQnb/documentation/images/template-builder.png?fit=max\&auto=format\&n=m-Fi8rWVvgP3bQnb\&q=85\&s=19335b2ed128ef9658babe8c9d33bef2) 1. Log in to your PassEntry account 2. Go to [**Pass Templates**](https://app.passentry.com/pass-templates) 3. Click **Create template** 4. Configure the template: * Template type (loyalty, event, generic, transit) * Visual branding (images and colours). Learn more: [Designing passes](/documentation/passes/design/guide) * Pass front fields (for example: membership ID, seat number, expiry date) Optional (can be added later): * Pass back fields (for example: clickable website link, support email or phone, map directions) * App links to your iOS and Android apps * Locations and beacons. Learn more: [Geofencing](/documentation/passes/geofencing/guide) * Barcode, QR code or NFC. NFC requires prior approval. Learn more: [Issuing NFC passes](/faq/guide#how-do-i-issue-production-nfc-passes) * Custom pass issuance form. Learn more: [Custom pass issuance forms](/documentation/guides/custom-download-forms/guide) 5. Save the template *** ## Step 2: Issue a pass Once a pass template exists, you can issue individual passes from it. ### Issue a pass in the dashboard ![](https://mintcdn.com/passentry-0/pLUpGEBkR-mM9dIa/documentation/images/create-pass.png?fit=max\&auto=format\&n=pLUpGEBkR-mM9dIa\&q=85\&s=74a79a5f4231b9873ca591c8fd44f5a7) 1. Open the PassEntry dashboard 2. Go to [**Passes**](https://app.passentry.com/passes) 3. Click **Create a pass** 4. Select a pass template you created in the previous step 5. Fill in the required pass data 6. Create the pass After the pass is created, a **pass download link** is generated. You can share this link with the user to add the pass to Apple Wallet or Google Wallet. > [!NOTE] > > If you want to issue passes programmatically, see the [API reference](/api-reference/v1/passes/create-pass). *** ## Step 3: Add the pass to a wallet ![](https://mintcdn.com/passentry-0/m-Fi8rWVvgP3bQnb/documentation/images/download-pass.png?fit=max\&auto=format\&n=m-Fi8rWVvgP3bQnb\&q=85\&s=4a7bfbe527268518a6c3272cb3b2e4e6) Copy the **pass download link** and either share it with the user or open it on your own device for testing. * When opened on a mobile device, the link opens Apple Wallet (iOS) or Google Wallet (Android) directly. * When opened on desktop, the link displays a QR code that can be scanned to add the pass to a mobile wallet. ![](https://mintcdn.com/passentry-0/m-Fi8rWVvgP3bQnb/documentation/images/download-page.png?fit=max\&auto=format\&n=m-Fi8rWVvgP3bQnb\&q=85\&s=471a4665ef13d61576a540eb8825c786) You can distribute the pass download link via confirmation pages, email or SMS or in-app links. Learn more: [Downloading passes](/documentation/passes/downloading-passes/guide) *** ## Step 4: Updating an existing pass Issued passes can be updated at any time. Updates are reflected automatically in the user’s wallet — there is no need to reinstall the pass. Pass updates can be triggered in two ways: * By changing the data of an individual pass (for example: balance, tier, status) * By updating the pass template (for example: adding a holiday banner or changing an offer), which updates **all existing passes** created from that template ### Update a pass via the dashboard ![](https://mintcdn.com/passentry-0/m-Fi8rWVvgP3bQnb/documentation/images/update-pass.png?fit=max\&auto=format\&n=m-Fi8rWVvgP3bQnb\&q=85\&s=4cd9c3d408114252e56788854994c1b9) 1. Open the PassEntry dashboard 2. Go to [**Passes**](https://app.passentry.com/passes) 3. Select a pass 4. Update any field (for example: balance or tier) 5. Save changes The update is **pushed to the wallet automatically**. You can verify the change by opening the pass in Apple Wallet or Google Wallet. In some cases, Google Wallet may require reopening the app or a short delay for the update to appear. > [!NOTE] > > If you want to update passes programmatically, see the [API reference](/api-reference/v1/passes/update-pass). *** ## Step 5: Sending notifications to pass holders You can send notifications to pass holders to inform them about updates such as new offers, subscription expiry or balance changes. Notifications can include clickable links. ### Send a notification from the dashboard ![](https://mintcdn.com/passentry-0/m-Fi8rWVvgP3bQnb/documentation/images/send-notification.png?fit=max\&auto=format\&n=m-Fi8rWVvgP3bQnb\&q=85\&s=496db360b0b2fa3991f1e0693197edde) 1. Go to **Passes** and open the pass you created in the previous step 2. Find the notification section in the pass preview 3. Enter the notification text 4. Send the notification To verify delivery, check for the push notification on the device where the pass is installed. The pass must be **active** and **added to a wallet** to receive notifications. > [!NOTE] > > Notifications can also be sent in bulk using CSV uploads or programmatically via the API. Learn more: * [Notifications guide](/documentation/passes/notifications/guide) * [Bulk notifications via CSV](/documentation/bulk-operations/batch-notifications/guide) * [Notifications API reference](/api-reference/v1/passes/send-notification) *** ## What’s next? Once you’ve issued your first pass, you can explore more advanced workflows: * **Bulk operations**. Create, update or notify many passes at once using CSV uploads. Learn more: * [Batch pass creation](/documentation/bulk-operations/batch-pass-creation/guide) * [Batch pass updates](/documentation/bulk-operations/batch-pass-update/guide) * [Batch notifications](/documentation/bulk-operations/batch-notifications/guide) * **Custom download pages and pass issuance forms**. Create a branded pass download page or a custom form to let users submit their data and claim passes themselves. Learn more: * [Custom download pages](/documentation/passes/downloading-passes/guide) * [Custom pass issuance forms](/documentation/guides/custom-download-forms/guide) * **API automation**. Automate pass issuance and updates from your backend. Learn more: [API reference](/api-reference/v1/introduction/authentication) * **Pass validation with the Reader app**. Install the PassEntry Reader app and validate passes in real time. Learn more: [PassEntry Reader app configuration](/documentation/guides/passentry-reader-app/guide) * **Zapier integrations**. Connect PassEntry to your existing tools without writing any code. Learn more: [Integrating with PassEntry via Zapier](/documentation/guides/zapier/guide) *** ## Need help? If you need assistance, contact [PassEntry support](mailto:support@passentry.com). We’ll be happy to help you get started.