Category / Section
Custom Connector to call API when pass installation status change
Published:
Updated:
Custom Connector for Pass Installation Status
It is possible to create a custom connector in The Wallet Crew (formerly Neostore) to call an external API whenever a pass is installed or uninstalled on Apple or Google Wallet.
This allows partners to synchronize installation status with external systems such as a CRM, analytics tool, or marketing automation platform.
Runtime Hooks
The platform exposes the following hooks on the runtime.wallet.passUpdater
endpoint:
OnPassInstalled
→ Triggered when a pass is added to Apple/Google WalletOnPassUninstalled
→ Triggered when a pass is removed from Apple/Google Wallet
Both methods receive the same parameters:
Parameter | Type | Description |
---|---|---|
passId |
string |
Unique identifier of the pass |
passType |
string |
Type of pass (loyalty, gift card, event ticket…) |
identifiers |
Record<string, any> |
Key/value identifiers defined on the pass (e.g. customerId ) |
device |
"apple" or "google" |
Wallet platform |
additionalInformation |
AdditionalPassInstallationInformation |
Includes registration statistics |
Example Implementation
Scripts can be placed in the server/scripts
folder under advanced configuration.
const API_URL = 'https://partner';
import { getSecret } from 'neo/secrets';
/**
* @typedef {Object} RegistrationInformation
* @property {number} activeRegistrationCount - Active registrations.
* @property {number} totalRegistrationCount - Total registrations.
**/
/**
* @typedef {Object} RegistrationSource
* @property {string[]} tags - list of tags specified by the SDK integration
* @property {string} medium - medium specified by the SDK integration or utm_medium
* @property {string} origin - url from where the pass is installed - could be overriden by the SDK integration
* @property {string} userAgent - optional UserAgent used to install the pass
*/
/**
* @typedef {Object} AdditionalPassInstallationInformation
* @property {RegistrationInformation} registrationInformation - Registration statistics.
* @property {RegistrationSource} registrationSource - Source of the installation when available
*/
/** methods triggered when a pass is installed
* @param {string} passId - unique identifier of the neostore pass
* @param {string} passType - pass type of the current pass
* @param {Record<string, any>} identifiers - key value pairs of identifiers of the pass
* @param {"apple"|"google"} device - named of the device
* @param {AdditionalPassInstallationInformation} additionalInformation - additionalInformation related to this pass installation */
async function onPassInstalled(passId, passType, identifiers, device, additionalInformation) {
await onPassInstallationStatusChanged(passId, passType, identifiers, device, true);
}
/** methods triggered when a pass is uninstalled
* @param {string} passId - unique identifier of the neostore pass
* @param {string} passType - pass type of the current pass
* @param {Record<string, any>} identifiers - key value pairs of identifiers of the pass
* @param {"apple"|"google"} device - named of the device
* @param {AdditionalPassInstallationInformation} additionalInformation - additionalInformation related to this pass installation
*/
async function onPassUninstalled(passId, passType, identifiers, device, additionalInformation) {
await onPassInstallationStatusChanged(passId, passType, identifiers, device, false);
}
async function onPassInstallationStatusChanged(passId, passType, identifiers, device, isInstalled) {
const customerId = identifiers["customerId"]; // or any other external identifier
const API_SECRET = await getSecret('PARTNER-API-SECRET');
// ⚠️ Note: fetch is not standard here. It uses PascalCase options and object-based Body.
await fetch(API_URL + '/wallet/installation', {
Method: "POST",
Headers: {
"Content-Type": "application/x-www-form-urlencoded",
"X-API-KEY": API_SECRET
},
Body: {
customerId,
isInstalled,
device
},
ThrowOnError: false
});
}
export default function (context) {
context.register('runtime.wallet.passUpdater', {
OnPassInstalled: onPassInstalled,
OnPassUninstalled: onPassUninstalled
});
}
Minimal Example
For testing or logging only:
async function onPassInstalled(passId, passType, identifiers, device) {
console.log(`Pass ${passId} installed on ${device}`);
}
async function onPassUninstalled(passId, passType, identifiers, device) {
console.log(`Pass ${passId} uninstalled from ${device}`);
}
export default function (context) {
context.register('runtime.wallet.passUpdater', {
OnPassInstalled: onPassInstalled,
OnPassUninstalled: onPassUninstalled
});
}
When Are Events Triggered?
OnPassInstalled
→ fired when a user successfully adds a pass to Apple or Google Wallet.OnPassUninstalled
→ fired when a user removes the pass from their wallet.- Events are reliable: the platform ensures correct delivery even under high load.
Use Cases
- CRM Synchronization → Update customer records with installation status.
- Marketing Automation → Trigger campaigns when a pass is added or removed.
- Analytics Tracking → Send events to BI tools to measure adoption.
Partners are free to adapt the payload to their own API contract.
Notes
fetch
is not the standard browser fetch — it accepts PascalCase options (Method
,Headers
,Body
,ThrowOnError
).- Authentication is flexible: you may use API keys (via
getSecret
) or OAuth with the customfetch
. - There are no platform restrictions: execution is safe and fully managed.
👉 With this approach, partners can easily integrate real-time pass installation insights into any external system.