# Pass

## Retrieve passes with optional filtering, sorting, and pagination.

> \*\*Authorization:\*\* Requires \`Pass.Read\` scope.\
> \
> \*\*Filtering:\*\* By identifiers (e.g., \`identifiers.email\`), metadata fields (e.g., \`metadata.loyaltyTier\`), pass type, or installation status (apple, google).\
> \
> \*\*Sorting:\*\* By id, passType, creationDate, lastUpdateDate, apple, google, or any identifier/metadata field.\
> \
> \*\*Pagination:\*\* Zero-based \`pageIndex\` and \`pageSize\`. Total count in \`x-pagination-total\` header.\
> \
> \*\*Use Cases:\*\* Search passes by customer attributes; monitor installation status; filter by loyalty tier or campaign flag.\
> \
> \*\*Example — list loyalty passes, page 0:\*\*\
> \`\`\`\
> GET /api/{tenantId}/passes?pageIndex=0\&pageSize=20\
> &#x20;   \&filter\[0].field=passType\&filter\[0].operator=equals\&filter\[0].value=loyalty\
> &#x20;   \&sortBy\[0].field=creationDate\&sortBy\[0].direction=DESC\
> \`\`\`\
> \*\*Example — passes installed on Apple Wallet:\*\*\
> \`\`\`\
> GET /api/{tenantId}/passes?filter\[0].field=installationStatus\&filter\[0].operator=contains\&filter\[0].value=apple\
> \`\`\`

````json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["ScopedAuthorizeRequirement"]},{"apiKey":["ScopedAuthorizeRequirement"]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"FilterModel":{"type":"object","properties":{"field":{"type":"string","description":"Field to filter by (identifiers.*, metadata.*, passType, installationStatus)."},"operator":{"description":"Comparison operator to apply.","$ref":"#/components/schemas/FilterModelOperator"},"value":{"type":["null","array"],"items":{"type":"string"},"description":"Filter values. Single-element for most operators; multiple elements for `in` and `notIn`.\nThe query parameter name is `value` (repeated for multiple values).\n<example>\nSingle value (equals, contains, startsWith, …):\n```\nGET /passes?filter[0].field=passType&filter[0].operator=equals&filter[0].value=boarding\n```\nMultiple values (in / notIn):\n```\nGET /passes?filter[0].field=passType&filter[0].operator=in&filter[0].value=boarding&filter[0].value=loyalty\n```\nMultiple filters combined:\n```\nGET /passes?filter[0].field=passType&filter[0].operator=equals&filter[0].value=boarding\n           &filter[1].field=identifiers.customerId&filter[1].operator=startsWith&filter[1].value=CUST-\n```</example>"}},"additionalProperties":false,"description":"Filter specification for pass queries."},"FilterModelOperator":{"enum":["Contains","StartsWith","EndsWith","Equals","IsEmpty","IsNotEmpty","NotEquals","In","NotIn"],"type":"string","description":"Filter operators for pass list queries."},"SortModel":{"type":"object","properties":{"field":{"type":"string","description":"Field to sort by."},"direction":{"description":"Sort direction (ASC or DESC).","$ref":"#/components/schemas/SortModelDirection"}},"additionalProperties":false,"description":"Sort specification for pass queries."},"SortModelDirection":{"enum":[0,1],"type":"integer","description":"Sort direction for pass list queries.","format":"int32"},"Pass":{"type":"object","properties":{"id":{"type":"string","description":"Platform-assigned unique pass identifier. This is a random alphanumeric string generated at creation time and cannot be set by callers (e.g., `xK9mP2nQr7sT`)."},"secret":{"type":"string","description":"Platform-generated opaque token used internally to authenticate pass delivery. Read-only; treat as confidential."},"creationDate":{"type":"string","description":"Timestamp when the pass was created by the platform. Read-only.","format":"date-time"},"lastUpdateDate":{"type":"string","description":"Timestamp of the most recent update. Equals `CreationDate` when no update has occurred. Read-only.","format":"date-time"},"passType":{"type":"string","description":"Type of pass, matching a file in the tenant `server/passes/` configuration."},"identifiers":{"type":"object","additionalProperties":{"type":"string"},"description":"External identifiers keyed by system name (e.g., `shopify.customerId`)."},"metadata":{"type":"object","additionalProperties":{"type":"null"},"description":"Computed metadata fields managed by the platform."},"additionalData":{"type":"object","additionalProperties":{"type":"null"},"description":"Arbitrary key/value data persisted with the pass."},"providers":{"type":"object","additionalProperties":{},"description":"Installation details keyed by provider name (e.g., `apple`, `google`). Populated by the platform. Read-only."}},"additionalProperties":false,"description":"Represents a pass returned by the API."}}},"paths":{"/api/{tenantId}/passes":{"get":{"tags":["Pass"],"summary":"Retrieve passes with optional filtering, sorting, and pagination.","description":"**Authorization:** Requires `Pass.Read` scope.\n\n**Filtering:** By identifiers (e.g., `identifiers.email`), metadata fields (e.g., `metadata.loyaltyTier`), pass type, or installation status (apple, google).\n\n**Sorting:** By id, passType, creationDate, lastUpdateDate, apple, google, or any identifier/metadata field.\n\n**Pagination:** Zero-based `pageIndex` and `pageSize`. Total count in `x-pagination-total` header.\n\n**Use Cases:** Search passes by customer attributes; monitor installation status; filter by loyalty tier or campaign flag.\n\n**Example — list loyalty passes, page 0:**\n```\nGET /api/{tenantId}/passes?pageIndex=0&pageSize=20\n    &filter[0].field=passType&filter[0].operator=equals&filter[0].value=loyalty\n    &sortBy[0].field=creationDate&sortBy[0].direction=DESC\n```\n**Example — passes installed on Apple Wallet:**\n```\nGET /api/{tenantId}/passes?filter[0].field=installationStatus&filter[0].operator=contains&filter[0].value=apple\n```","parameters":[{"name":"pageIndex","in":"query","description":"Zero-based page index.","schema":{"type":"integer","format":"int32","default":0}},{"name":"pageSize","in":"query","description":"Number of passes per page.","schema":{"type":"integer","format":"int32","default":20}},{"name":"filter","in":"query","description":"Optional filters applied to identifiers, metadata, pass type, or installation status.","schema":{"type":"array","items":{"description":"Filter specification for pass queries.","$ref":"#/components/schemas/FilterModel"}}},{"name":"sortBy","in":"query","description":"Optional sort descriptors. When absent, results are unordered.","schema":{"type":"array","items":{"description":"Sort specification for pass queries.","$ref":"#/components/schemas/SortModel"}}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Paged list of passes returned successfully.","content":{"text/plain":{"schema":{"type":"array","items":{"description":"Represents a pass returned by the API.","$ref":"#/components/schemas/Pass"}}},"application/json":{"schema":{"type":"array","items":{"description":"Represents a pass returned by the API.","$ref":"#/components/schemas/Pass"}}},"text/json":{"schema":{"type":"array","items":{"description":"Represents a pass returned by the API.","$ref":"#/components/schemas/Pass"}}}}}}}}}}
````

## Create a new pass.

> \*\*Authorization:\*\* Requires \`Pass.Write\` scope OR valid identifier validation (HMAC signature, secret, or allowlisted unsigned).\
> \
> \*\*Security:\*\* Each identifier must validate via: (1) HMAC (key.hmac), (2) registered secret (key.secret), (3) security allowlist, or (4) Pass.Write scope. Omit identifiers if JWT contains customer claims.\
> \
> \*\*Pass Type:\*\* Template to use. Must match file in tenant \`server/passes/\` config.\
> \
> \*\*Use Cases:\*\* API-based pass creation; customer self-service with HMAC; bulk imports.\
> \
> \*\*Example — create with HMAC-signed identifier:\*\*\
> \`\`\`\
> POST /api/{tenantId}/passes?passType=loyalty\
> &#x20;           \
> {\
> &#x20; "identifiers": {\
> &#x20;   "shopify.customerId": "12345",\
> &#x20;   "shopify.customerId.hmac": "{hmac-of-12345}"\
> &#x20; },\
> &#x20; "additionalData": { "loyaltyTier": "silver" }\
> }\
> \`\`\`\
> \*\*Example — create with Pass.Write scope (no HMAC required):\*\*\
> \`\`\`\
> POST /api/{tenantId}/passes?passType=loyalty\
> &#x20;           \
> {\
> &#x20; "identifiers": { "shopify.customerId": "12345" },\
> &#x20; "additionalData": { "loyaltyTier": "silver" }\
> }\
> \`\`\`

````json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["ScopedAuthorizeRequirement"]},{"apiKey":["ScopedAuthorizeRequirement"]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"CreatePassData":{"type":"object","allOf":[{"$ref":"#/components/schemas/PassData"}],"properties":{"extensions":{"type":["null","object"],"additionalProperties":{"type":"null"},"description":"Extensibility data related to this pass."}},"additionalProperties":false,"description":"Data payload for pass creation operations."},"PassData":{"type":"object","properties":{"identifiers":{"type":"object","additionalProperties":{"type":"string"},"description":"External identifiers of the customer for this pass. Keys must not start with `id.`; common examples are `y2.customerId` or `shopify.customerId`.\nUse an empty value to remove an identifier. Leave the collection empty to keep existing identifiers unchanged."},"additionalData":{"type":["null","object"],"additionalProperties":{"type":"null"},"description":"Arbitrary data to persist with the pass (for example, loyalty tier, store code, or campaign flags)."}},"additionalProperties":false},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/passes":{"post":{"tags":["Pass"],"summary":"Create a new pass.","description":"**Authorization:** Requires `Pass.Write` scope OR valid identifier validation (HMAC signature, secret, or allowlisted unsigned).\n\n**Security:** Each identifier must validate via: (1) HMAC (key.hmac), (2) registered secret (key.secret), (3) security allowlist, or (4) Pass.Write scope. Omit identifiers if JWT contains customer claims.\n\n**Pass Type:** Template to use. Must match file in tenant `server/passes/` config.\n\n**Use Cases:** API-based pass creation; customer self-service with HMAC; bulk imports.\n\n**Example — create with HMAC-signed identifier:**\n```\nPOST /api/{tenantId}/passes?passType=loyalty\n            \n{\n  \"identifiers\": {\n    \"shopify.customerId\": \"12345\",\n    \"shopify.customerId.hmac\": \"{hmac-of-12345}\"\n  },\n  \"additionalData\": { \"loyaltyTier\": \"silver\" }\n}\n```\n**Example — create with Pass.Write scope (no HMAC required):**\n```\nPOST /api/{tenantId}/passes?passType=loyalty\n            \n{\n  \"identifiers\": { \"shopify.customerId\": \"12345\" },\n  \"additionalData\": { \"loyaltyTier\": \"silver\" }\n}\n```","parameters":[{"name":"passType","in":"query","description":"Type of the pass to create. Must match a file in the tenant `server/passes/` configuration.","schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Data related to this pass.","content":{"application/json":{"schema":{"description":"Data payload for pass creation operations.","$ref":"#/components/schemas/CreatePassData"}},"text/json":{"schema":{"description":"Data payload for pass creation operations.","$ref":"#/components/schemas/CreatePassData"}},"application/*+json":{"schema":{"description":"Data payload for pass creation operations.","$ref":"#/components/schemas/CreatePassData"}}}},"responses":{"200":{"description":"Pass created successfully.","content":{"text/plain":{"schema":{"type":"string"}},"application/json":{"schema":{"type":"string"}},"text/json":{"schema":{"type":"string"}}}},"401":{"description":"Identifiers cannot be validated and caller lacks Pass.Write scope.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
````

## Update a pass and optionally its type.

> \*\*Authorization:\*\* Requires \`Pass.Write\` scope.\
> \
> \*\*Identification:\*\* Use internal \`id\` (e.g., \`id=Ed34kg3oA47\`) or external identifiers with \`id.\` prefix (e.g., \`id.y2.customerId=1233332\`). Multiple identifiers must match exactly one pass.\
> \
> \*\*Data:\*\* Merged with existing data. Empty/null removes fields; omitted fields unchanged.\
> \
> \*\*Metadata:\*\* Set \`options.UpdateMetadata=true\` for recomputation (slower). Set \`options.BypassQueue=true\` for synchronous updates instead of queueing.\
> \
> \*\*Type:\*\* Optionally convert pass to different type.\
> \
> \*\*Use Cases:\*\* Update identifiers/metadata; push notifications; type conversion; bulk updates; urgent changes.\
> \
> \*\*Example — update by platform pass ID:\*\*\
> \`\`\`\
> PATCH /api/{tenantId}/passes?id=xK9mP2nQr7sT\
> &#x20;           \
> {\
> &#x20; "additionalData": { "loyaltyTier": "gold" },\
> &#x20; "options": { "updateMetadata": true }\
> }\
> \`\`\`\
> \*\*Example — update by external identifier:\*\*\
> \`\`\`\
> PATCH /api/{tenantId}/passes?id.shopify.customerId=12345\
> &#x20;           \
> {\
> &#x20; "identifiers": { "email": "<user@example.com>" },\
> &#x20; "additionalData": { "loyaltyTier": "gold" },\
> &#x20; "options": { "updateMetadata": false }\
> }\
> \`\`\`

````json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["ScopedAuthorizeRequirement"]},{"apiKey":["ScopedAuthorizeRequirement"]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"SingleUpdatePassData":{"type":"object","allOf":[{"$ref":"#/components/schemas/SingleUpdatePassDataOptionsUpdatePassData"}],"properties":{"bypassQueue":{"type":"boolean","description":"Indicates whether the push update should bypass the queue and run immediately. Queuing helps protect the system load and should only be bypassed when required.","default":false,"deprecated":true}},"additionalProperties":false,"description":"Data payload for single pass update operations."},"SingleUpdatePassDataOptionsUpdatePassData":{"type":"object","properties":{"identifiers":{"type":"object","additionalProperties":{"type":"string"},"description":"External identifiers of the customer for this pass. Keys must not start with `id.`; common examples are `y2.customerId` or `shopify.customerId`.\nUse an empty value to remove an identifier. Leave the collection empty to keep existing identifiers unchanged."},"additionalData":{"type":["null","object"],"additionalProperties":{"type":"null"},"description":"Arbitrary data to persist with the pass (for example, loyalty tier, store code, or campaign flags)."},"passType":{"type":["null","string"],"description":"Optional pass type to convert the pass to."},"updateMetadata":{"type":"boolean","description":"Specifies if passes metadata should be updated. Updating metadata is time consuming and could be avoided for notification only push update","default":false,"deprecated":true},"options":{"description":"Options for single pass update operations. Metadata recomputation is enabled by default.\nUse Neo.Web.Api.Controllers.PassController.SingleUpdatePassDataOptions.BypassQueue to apply the update synchronously instead of queueing.","$ref":"#/components/schemas/SingleUpdatePassDataOptions"}},"additionalProperties":false},"SingleUpdatePassDataOptions":{"type":"object","allOf":[{"description":"Options used when updating passes.","$ref":"#/components/schemas/UpdatePassDataOptions"}],"properties":{"bypassQueue":{"type":"boolean","description":"Indicates whether the push update should bypass the queue and run immediately. Queuing helps protect the system load and should only be bypassed when required.","default":false}},"additionalProperties":false,"description":"Options for single pass update operations. Metadata recomputation is enabled by default.\nUse Neo.Web.Api.Controllers.PassController.SingleUpdatePassDataOptions.BypassQueue to apply the update synchronously instead of queueing."},"UpdatePassDataOptions":{"type":"object","properties":{"updateMetadata":{"type":"boolean","description":"When true, recompute and persist pass metadata. Updating metadata is slower and is usually unnecessary for notification-only updates."},"correlationId":{"type":["null","string"],"description":"Groups related updates under the same correlationId. Useful for batch updates (for example nightly jobs) to make retries and logs traceable."}},"additionalProperties":false,"description":"Options used when updating passes."},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/passes":{"patch":{"tags":["Pass"],"summary":"Update a pass and optionally its type.","description":"**Authorization:** Requires `Pass.Write` scope.\n\n**Identification:** Use internal `id` (e.g., `id=Ed34kg3oA47`) or external identifiers with `id.` prefix (e.g., `id.y2.customerId=1233332`). Multiple identifiers must match exactly one pass.\n\n**Data:** Merged with existing data. Empty/null removes fields; omitted fields unchanged.\n\n**Metadata:** Set `options.UpdateMetadata=true` for recomputation (slower). Set `options.BypassQueue=true` for synchronous updates instead of queueing.\n\n**Type:** Optionally convert pass to different type.\n\n**Use Cases:** Update identifiers/metadata; push notifications; type conversion; bulk updates; urgent changes.\n\n**Example — update by platform pass ID:**\n```\nPATCH /api/{tenantId}/passes?id=xK9mP2nQr7sT\n            \n{\n  \"additionalData\": { \"loyaltyTier\": \"gold\" },\n  \"options\": { \"updateMetadata\": true }\n}\n```\n**Example — update by external identifier:**\n```\nPATCH /api/{tenantId}/passes?id.shopify.customerId=12345\n            \n{\n  \"identifiers\": { \"email\": \"user@example.com\" },\n  \"additionalData\": { \"loyaltyTier\": \"gold\" },\n  \"options\": { \"updateMetadata\": false }\n}\n```","parameters":[{"name":"identifiers","in":"query","description":"identifier of the pass. To update a pass with a Wallet Crew internal id only specify `id` (example : `\"id\": \"Ed34kg3oA47\"`) to update a pass with external identifier prefix the key with `id.` (example : `\"id.y2.customerId\": \"1233332\"`) \n\nWhen multiple external identifiers is submitted all identifiers should be found. \nIf more than one pass is found an exception will be thrown.","schema":{"type":"object","additionalProperties":{"type":"string"}}},{"name":"passType","in":"query","description":"type of the pass to update. type name should be one of the file in the `server/passes/` tenant configuration.","schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"data related to this pass. This data will be merge with the existing data.","content":{"application/json":{"schema":{"description":"Data payload for single pass update operations.","$ref":"#/components/schemas/SingleUpdatePassData"}},"text/json":{"schema":{"description":"Data payload for single pass update operations.","$ref":"#/components/schemas/SingleUpdatePassData"}},"application/*+json":{"schema":{"description":"Data payload for single pass update operations.","$ref":"#/components/schemas/SingleUpdatePassData"}}}},"responses":{"200":{"description":"Pass update enqueued or applied successfully."},"404":{"description":"No pass found matching the provided identifiers.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
````

## Retrieve the specified pass for the specified device.

> Download an Apple Wallet pass:\
> \`\`\`\
> GET /api/{tenantId}/passes/xK9mP2nQr7sT?device=apple\
> \`\`\`\
> Auto-redirect to the appropriate wallet based on user-agent:\
> \`\`\`\
> GET /api/{tenantId}/passes/xK9mP2nQr7sT?device=auto\
> \`\`\`\
> Track the installation source (email campaign, email medium):\
> \`\`\`\
> GET /api/{tenantId}/passes/xK9mP2nQr7sT?device=apple\&neo.src=email-campaign|email\
> \`\`\`

````json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"paths":{"/api/{tenantId}/passes/{passId}":{"get":{"tags":["Pass"],"summary":"Retrieve the specified pass for the specified device.","description":"Download an Apple Wallet pass:\n```\nGET /api/{tenantId}/passes/xK9mP2nQr7sT?device=apple\n```\nAuto-redirect to the appropriate wallet based on user-agent:\n```\nGET /api/{tenantId}/passes/xK9mP2nQr7sT?device=auto\n```\nTrack the installation source (email campaign, email medium):\n```\nGET /api/{tenantId}/passes/xK9mP2nQr7sT?device=apple&neo.src=email-campaign|email\n```","parameters":[{"name":"passId","in":"path","description":"Platform-assigned pass identifier (random alphanumeric string, e.g. `xK9mP2nQr7sT`).","required":true,"schema":{"pattern":"^[\\w-]{5,50}$","type":"string"}},{"name":"device","in":"query","description":"Target platform: `apple`, `google`, `preview`, or `auto`.","schema":{"type":"string"}},{"name":"Tags","in":"query","description":"Source tags for categorizing installation source.","schema":{"type":"array","items":{"type":"string"}}},{"name":"Medium","in":"query","description":"Installation medium (e.g., email, sms, app).","schema":{"type":"string"}},{"name":"Origin","in":"query","description":"Installation origin or referrer.","schema":{"type":"string"}},{"name":"UserAgent","in":"query","description":"User agent string from the installation request.","schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}}}}
````

## Update a pass using its passId.

> \*\*Authorization:\*\* Requires \`Pass.Write\` scope.\
> \
> \*\*Identification:\*\* Uses the pass's unique \`passId\` directly.\
> \
> \*\*Data:\*\* Merged with existing data. Empty/null removes fields; omitted fields unchanged.\
> \
> \*\*Metadata:\*\* Set \`options.UpdateMetadata=true\` for recomputation (slower). Set \`options.BypassQueue=true\` for synchronous updates instead of queueing.\
> \
> \*\*Type:\*\* Optionally convert pass to different type.\
> \
> \*\*Use Cases:\*\* Update passes by internal ID; push notifications; type conversion; urgent changes.\
> \
> \*\*Example — update metadata and push notification:\*\*\
> \`\`\`\
> PATCH /api/{tenantId}/passes/xK9mP2nQr7sT\
> &#x20;           \
> {\
> &#x20; "additionalData": { "loyaltyTier": "platinum", "points": 5000 },\
> &#x20; "options": { "updateMetadata": true, "bypassQueue": false }\
> }\
> \`\`\`\
> \*\*Example — urgent update, bypass queue:\*\*\
> \`\`\`\
> PATCH /api/{tenantId}/passes/xK9mP2nQr7sT\
> &#x20;           \
> {\
> &#x20; "additionalData": { "boardingStatus": "boarding" },\
> &#x20; "options": { "updateMetadata": true, "bypassQueue": true }\
> }\
> \`\`\`

````json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["ScopedAuthorizeRequirement"]},{"apiKey":["ScopedAuthorizeRequirement"]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"SingleUpdatePassData":{"type":"object","allOf":[{"$ref":"#/components/schemas/SingleUpdatePassDataOptionsUpdatePassData"}],"properties":{"bypassQueue":{"type":"boolean","description":"Indicates whether the push update should bypass the queue and run immediately. Queuing helps protect the system load and should only be bypassed when required.","default":false,"deprecated":true}},"additionalProperties":false,"description":"Data payload for single pass update operations."},"SingleUpdatePassDataOptionsUpdatePassData":{"type":"object","properties":{"identifiers":{"type":"object","additionalProperties":{"type":"string"},"description":"External identifiers of the customer for this pass. Keys must not start with `id.`; common examples are `y2.customerId` or `shopify.customerId`.\nUse an empty value to remove an identifier. Leave the collection empty to keep existing identifiers unchanged."},"additionalData":{"type":["null","object"],"additionalProperties":{"type":"null"},"description":"Arbitrary data to persist with the pass (for example, loyalty tier, store code, or campaign flags)."},"passType":{"type":["null","string"],"description":"Optional pass type to convert the pass to."},"updateMetadata":{"type":"boolean","description":"Specifies if passes metadata should be updated. Updating metadata is time consuming and could be avoided for notification only push update","default":false,"deprecated":true},"options":{"description":"Options for single pass update operations. Metadata recomputation is enabled by default.\nUse Neo.Web.Api.Controllers.PassController.SingleUpdatePassDataOptions.BypassQueue to apply the update synchronously instead of queueing.","$ref":"#/components/schemas/SingleUpdatePassDataOptions"}},"additionalProperties":false},"SingleUpdatePassDataOptions":{"type":"object","allOf":[{"description":"Options used when updating passes.","$ref":"#/components/schemas/UpdatePassDataOptions"}],"properties":{"bypassQueue":{"type":"boolean","description":"Indicates whether the push update should bypass the queue and run immediately. Queuing helps protect the system load and should only be bypassed when required.","default":false}},"additionalProperties":false,"description":"Options for single pass update operations. Metadata recomputation is enabled by default.\nUse Neo.Web.Api.Controllers.PassController.SingleUpdatePassDataOptions.BypassQueue to apply the update synchronously instead of queueing."},"UpdatePassDataOptions":{"type":"object","properties":{"updateMetadata":{"type":"boolean","description":"When true, recompute and persist pass metadata. Updating metadata is slower and is usually unnecessary for notification-only updates."},"correlationId":{"type":["null","string"],"description":"Groups related updates under the same correlationId. Useful for batch updates (for example nightly jobs) to make retries and logs traceable."}},"additionalProperties":false,"description":"Options used when updating passes."},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/passes/{passId}":{"patch":{"tags":["Pass"],"summary":"Update a pass using its passId.","description":"**Authorization:** Requires `Pass.Write` scope.\n\n**Identification:** Uses the pass's unique `passId` directly.\n\n**Data:** Merged with existing data. Empty/null removes fields; omitted fields unchanged.\n\n**Metadata:** Set `options.UpdateMetadata=true` for recomputation (slower). Set `options.BypassQueue=true` for synchronous updates instead of queueing.\n\n**Type:** Optionally convert pass to different type.\n\n**Use Cases:** Update passes by internal ID; push notifications; type conversion; urgent changes.\n\n**Example — update metadata and push notification:**\n```\nPATCH /api/{tenantId}/passes/xK9mP2nQr7sT\n            \n{\n  \"additionalData\": { \"loyaltyTier\": \"platinum\", \"points\": 5000 },\n  \"options\": { \"updateMetadata\": true, \"bypassQueue\": false }\n}\n```\n**Example — urgent update, bypass queue:**\n```\nPATCH /api/{tenantId}/passes/xK9mP2nQr7sT\n            \n{\n  \"additionalData\": { \"boardingStatus\": \"boarding\" },\n  \"options\": { \"updateMetadata\": true, \"bypassQueue\": true }\n}\n```","parameters":[{"name":"passId","in":"path","description":"The unique identifier of the pass to update.","required":true,"schema":{"pattern":"^[\\w-]{5,50}$","type":"string"}},{"name":"passType","in":"query","description":"Optional type of the pass to update. Type name should match a file in the `server/passes/` tenant configuration.","schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Data payload containing identifiers, additional data, and update options.","content":{"application/json":{"schema":{"description":"Data payload for single pass update operations.","$ref":"#/components/schemas/SingleUpdatePassData"}},"text/json":{"schema":{"description":"Data payload for single pass update operations.","$ref":"#/components/schemas/SingleUpdatePassData"}},"application/*+json":{"schema":{"description":"Data payload for single pass update operations.","$ref":"#/components/schemas/SingleUpdatePassData"}}}},"responses":{"200":{"description":"Pass update queued or completed successfully."},"404":{"description":"Pass with the specified passId not found.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
````

## Retrieve a WebSocket URL for pass-level notifications.

> \`\`\`\
> GET /api/{tenantId}/passes/xK9mP2nQr7sT/connect\
> &#x20;           \
> \`\`\`

````json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"paths":{"/api/{tenantId}/passes/{passId}/connect":{"get":{"tags":["Pass"],"summary":"Retrieve a WebSocket URL for pass-level notifications.","description":"```\nGET /api/{tenantId}/passes/xK9mP2nQr7sT/connect\n            \n```","parameters":[{"name":"passId","in":"path","description":"Platform-assigned pass identifier (random alphanumeric string, e.g. `xK9mP2nQr7sT`).","required":true,"schema":{"pattern":"^[\\w-]{5,50}$","type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"WebSocket URL returned successfully.","content":{"text/plain":{"schema":{"description":"Represents a Web PubSub connection response for a pass.","$ref":"#/components/schemas/PassConnectResponse"}},"application/json":{"schema":{"description":"Represents a Web PubSub connection response for a pass.","$ref":"#/components/schemas/PassConnectResponse"}},"text/json":{"schema":{"description":"Represents a Web PubSub connection response for a pass.","$ref":"#/components/schemas/PassConnectResponse"}}}},"404":{"description":"Pass not found.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}},"components":{"schemas":{"PassConnectResponse":{"type":"object","properties":{"url":{"type":"string","description":"WebSocket URL to connect for pass notifications."}},"additionalProperties":false,"description":"Represents a Web PubSub connection response for a pass."},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}}}
````

## Retrieve aggregated pass data from all registered data providers.

> \`\`\`\
> GET /api/{tenantId}/passes/xK9mP2nQr7sT/data\
> &#x20;           \
> \`\`\`

````json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"paths":{"/api/{tenantId}/passes/{passId}/data":{"get":{"tags":["Pass"],"summary":"Retrieve aggregated pass data from all registered data providers.","description":"```\nGET /api/{tenantId}/passes/xK9mP2nQr7sT/data\n            \n```","parameters":[{"name":"passId","in":"path","description":"Platform-assigned pass identifier (random alphanumeric string, e.g. `xK9mP2nQr7sT`).","required":true,"schema":{"pattern":"^[\\w-]{5,50}$","type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}}}}
````

## Retrieve the raw persisted pass document for diagnostics.

> \`\`\`\
> GET /api/{tenantId}/passes/xK9mP2nQr7sT/data/raw\
> &#x20;           \
> \`\`\`

````json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"paths":{"/api/{tenantId}/passes/{passId}/data/raw":{"get":{"tags":["Pass"],"summary":"Retrieve the raw persisted pass document for diagnostics.","description":"```\nGET /api/{tenantId}/passes/xK9mP2nQr7sT/data/raw\n            \n```","parameters":[{"name":"passId","in":"path","description":"Platform-assigned pass identifier (random alphanumeric string, e.g. `xK9mP2nQr7sT`).","required":true,"schema":{"pattern":"^[\\w-]{5,50}$","type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Raw pass document returned."}}}}}}
````

## PUT /api/{tenantId}/passes/{passId}/notification

> Send a notification to the pass identified by its pass identifier

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["ScopedAuthorizeRequirement"]},{"apiKey":["ScopedAuthorizeRequirement"]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"NotifyRequest":{"type":"object","properties":{"content":{"type":["null","string"],"description":"Could be null if LocalizedContent is specified"},"localizedContent":{"type":["null","object"],"additionalProperties":{"type":"string"},"description":"Localized notification content by language code.\nKey: ISO 639-1 code or \"iso2-region\" (e.g., \"en\", \"en-US\").\nValue: The notification content for that language.","propertyNames":{"pattern":"^[a-z]{2}(-[A-Z]{2})?$"}},"options":{"$ref":"#/components/schemas/NotifyRequestOptions"}},"additionalProperties":false},"NotifyRequestOptions":{"type":"object","properties":{"updateData":{"type":"boolean","description":"Indicate if pass data should also be updated when the notification is sent.","default":false},"correlationId":{"maxLength":100,"minLength":10,"type":["null","string"],"description":"Correlation identifier to group and trace related API calls."}},"additionalProperties":false},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/passes/{passId}/notification":{"put":{"tags":["Pass"],"summary":"Send a notification to the pass identified by its pass identifier","parameters":[{"name":"passId","in":"path","description":"The internal identifier of the pass","required":true,"schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"The notification content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotifyRequest"}},"text/json":{"schema":{"$ref":"#/components/schemas/NotifyRequest"}},"application/*+json":{"schema":{"$ref":"#/components/schemas/NotifyRequest"}}}},"responses":{"200":{"description":"OK"},"404":{"description":"Not Found","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
```

## Redirect to a web page that lets the user view and download the pass.

> \`\`\`\
> GET /api/{tenantId}/passes/xK9mP2nQr7sT/view\
> &#x20;           \
> \`\`\`\
> &#x20;           Additional query parameters are forwarded to the view page.

````json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"paths":{"/api/{tenantId}/passes/{passId}/view":{"get":{"tags":["Pass"],"summary":"Redirect to a web page that lets the user view and download the pass.","description":"```\nGET /api/{tenantId}/passes/xK9mP2nQr7sT/view\n            \n```\n            Additional query parameters are forwarded to the view page.","parameters":[{"name":"passId","in":"path","description":"Platform-assigned pass identifier (random alphanumeric string, e.g. `xK9mP2nQr7sT`).","required":true,"schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"302":{"description":"Found"},"404":{"description":"Not Found","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}},"components":{"schemas":{"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}}}
````

## Retrieve multiple passes for the specified device.

> Download a bundle of Apple Wallet passes:\
> \`\`\`\
> GET /api/{tenantId}/passes/xK9mP2nQr7sT,a3Bc4dEf5gHi?device=apple\
> \`\`\`\
> Retrieve JSON for Google Wallet:\
> \`\`\`\
> GET /api/{tenantId}/passes/xK9mP2nQr7sT,a3Bc4dEf5gHi?device=google\
> \`\`\`

````json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"paths":{"/api/{tenantId}/passes/{passIds}":{"get":{"tags":["Pass"],"summary":"Retrieve multiple passes for the specified device.","description":"Download a bundle of Apple Wallet passes:\n```\nGET /api/{tenantId}/passes/xK9mP2nQr7sT,a3Bc4dEf5gHi?device=apple\n```\nRetrieve JSON for Google Wallet:\n```\nGET /api/{tenantId}/passes/xK9mP2nQr7sT,a3Bc4dEf5gHi?device=google\n```","parameters":[{"name":"passIds","in":"path","description":"Comma-separated serial numbers of the passes.","required":true,"schema":{"pattern":"^[\\w-]{5,50},([\\w-]{5,50},?)+$","type":"array","items":{"type":"string"},"description":"Collection of pass identifiers with parsing support."}},{"name":"device","in":"query","description":"Target platform: `apple` or `google`.","schema":{"type":"string"}},{"name":"Tags","in":"query","description":"Source tags for categorizing installation source.","schema":{"type":"array","items":{"type":"string"}}},{"name":"Medium","in":"query","description":"Installation medium (e.g., email, sms, app).","schema":{"type":"string"}},{"name":"Origin","in":"query","description":"Installation origin or referrer.","schema":{"type":"string"}},{"name":"UserAgent","in":"query","description":"User agent string from the installation request.","schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}}}}
````

## Retrieve a Wallet Crew passId from external identifiers.

> Lookup with HMAC-signed identifier:\
> \`\`\`\
> GET /api/{tenantId}/passes/findPass?shopify.customerId=12345\&shopify.customerId.hmac={hmac}\
> \`\`\`\
> Lookup with Pass.Read scope (no HMAC required):\
> \`\`\`\
> GET /api/{tenantId}/passes/findPass?shopify.customerId=12345\
> \`\`\`

````json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"paths":{"/api/{tenantId}/passes/findPass":{"get":{"tags":["Pass"],"summary":"Retrieve a Wallet Crew passId from external identifiers.","description":"Lookup with HMAC-signed identifier:\n```\nGET /api/{tenantId}/passes/findPass?shopify.customerId=12345&shopify.customerId.hmac={hmac}\n```\nLookup with Pass.Read scope (no HMAC required):\n```\nGET /api/{tenantId}/passes/findPass?shopify.customerId=12345\n```","parameters":[{"name":"identifiers","in":"query","description":"- When `{key}` is supplied, either provide `{key}.hmac` signed with a tenant secret, ensure `security.yml` allows unsigned identifiers, or authenticate with a Pass.Read scope (API key or JWT).\n- Authenticated user claims are also considered when present.","schema":{"type":"object","additionalProperties":{"type":"string"}}},{"name":"passType","in":"query","description":"Optional pass type to restrict the search.","schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"text/plain":{"schema":{"type":"string"}},"application/json":{"schema":{"type":"string"}},"text/json":{"schema":{"type":"string"}}}},"204":{"description":"No Content"}}}}}}
````

## Retrieve passes using a supplier key or comma-separated pass IDs.

> \*\*Two Modes:\*\*\
> \- \*\*Supplier Mode:\*\* When \`provider\` specified, value passed to registered \`IPassSupplier\` (e.g., order lookup) returns associated pass IDs. Can optionally create missing passes.\
> \- \*\*Direct Mode:\*\* When \`provider\` null, value treated as comma-separated pass IDs.\
> \
> \*\*Preview:\*\* When \`includePreview=true\`, includes title, subtitle, colors, header text, enabled status for UI rendering.\
> \
> \*\*Use Cases:\*\* E-commerce (order → passes); subscriptions (subscription → passes); mobile preview data; loyalty programs (member → passes).\
> \
> \*\*Example — resolve via supplier:\*\*\
> \`\`\`\
> GET /api/{tenantId}/passes/getPasses?value=ORDER-12345\&provider=shopify\&includePreview=true\
> \`\`\`\
> \*\*Example — direct pass IDs lookup:\*\*\
> \`\`\`\
> GET /api/{tenantId}/passes/getPasses?value=xK9mP2nQr7sT,a3Bc4dEf5gHi\
> \`\`\`

````json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"paths":{"/api/{tenantId}/passes/getPasses":{"get":{"tags":["Pass"],"summary":"Retrieve passes using a supplier key or comma-separated pass IDs.","description":"**Two Modes:**\n- **Supplier Mode:** When `provider` specified, value passed to registered `IPassSupplier` (e.g., order lookup) returns associated pass IDs. Can optionally create missing passes.\n- **Direct Mode:** When `provider` null, value treated as comma-separated pass IDs.\n\n**Preview:** When `includePreview=true`, includes title, subtitle, colors, header text, enabled status for UI rendering.\n\n**Use Cases:** E-commerce (order → passes); subscriptions (subscription → passes); mobile preview data; loyalty programs (member → passes).\n\n**Example — resolve via supplier:**\n```\nGET /api/{tenantId}/passes/getPasses?value=ORDER-12345&provider=shopify&includePreview=true\n```\n**Example — direct pass IDs lookup:**\n```\nGET /api/{tenantId}/passes/getPasses?value=xK9mP2nQr7sT,a3Bc4dEf5gHi\n```","parameters":[{"name":"value","in":"query","description":"Key supplied to the provider to resolve passIds.","schema":{"type":"string"}},{"name":"provider","in":"query","description":"Provider name registered in DI. If null, value is treated as comma-separated passIds.","schema":{"type":"string"}},{"name":"includePreview","in":"query","description":"When true, include preview details for each pass.","schema":{"type":"boolean","default":false}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pass identifiers retrieved successfully.","content":{"text/plain":{"schema":{"type":"array","items":{}}},"application/json":{"schema":{"type":"array","items":{}}},"text/json":{"schema":{"type":"array","items":{}}}}}}}}}}
````

## PUT /api/{tenantId}/passes/notification

> Send a notification to the corresponding pass

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["ScopedAuthorizeRequirement"]},{"apiKey":["ScopedAuthorizeRequirement"]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"NotifyRequest":{"type":"object","properties":{"content":{"type":["null","string"],"description":"Could be null if LocalizedContent is specified"},"localizedContent":{"type":["null","object"],"additionalProperties":{"type":"string"},"description":"Localized notification content by language code.\nKey: ISO 639-1 code or \"iso2-region\" (e.g., \"en\", \"en-US\").\nValue: The notification content for that language.","propertyNames":{"pattern":"^[a-z]{2}(-[A-Z]{2})?$"}},"options":{"$ref":"#/components/schemas/NotifyRequestOptions"}},"additionalProperties":false},"NotifyRequestOptions":{"type":"object","properties":{"updateData":{"type":"boolean","description":"Indicate if pass data should also be updated when the notification is sent.","default":false},"correlationId":{"maxLength":100,"minLength":10,"type":["null","string"],"description":"Correlation identifier to group and trace related API calls."}},"additionalProperties":false},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/passes/notification":{"put":{"tags":["Pass"],"summary":"Send a notification to the corresponding pass","parameters":[{"name":"identifiers","in":"query","description":"identifier of the pass. To update a pass with a neostore internal id only specify `id` (example : `\"id\": \"Ed34kg3oA47\"`) to update a pass with external identifier prefix the key with `id.` (example : `\"id.y2.customerId\": \"1233332\"`) \n\nWhen multiple external identifiers is submitted all identifiers should be found. \nIf more than one pass is found an exception will be thrown.","schema":{"type":"object","additionalProperties":{"type":"string"}}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"The notification content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotifyRequest"}},"text/json":{"schema":{"$ref":"#/components/schemas/NotifyRequest"}},"application/*+json":{"schema":{"$ref":"#/components/schemas/NotifyRequest"}}}},"responses":{"200":{"description":"OK"},"404":{"description":"Not Found","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
```

## Push update for passes matching the filter.

> \*\*Authorization:\*\* Requires \`Pass.Write\` scope.\
> \
> \*\*Filtering:\*\* Same as GetPasses�identifiers, metadata, pass type, installation status.\
> \
> \*\*Async:\*\* Updates are queued. 200 response means scheduled, not complete. Monitor via statistics endpoint.\
> \
> \*\*Data Merge:\*\* Merged into each matching pass. Set \`UpdateMetadata=true\` for recomputation (slower). Adjust \`Throughput\` for concurrency.\
> \
> \*\*Bulk Operations:\*\* Ideal for campaigns, loyalty updates, seasonal offers. Use \`CorrelationId\` for tracking.\
> \
> \*\*Use Cases:\*\* Campaign push; loyalty tier changes; offer refresh; bulk metadata updates.\
> \
> \*\*Example — push a seasonal offer to all loyalty passes:\*\*\
> \`\`\`\
> POST /api/{tenantId}/passes/pushUpdate\
> &#x20;   ?filter\[0].field=passType\&filter\[0].operator=equals\&filter\[0].value=loyalty\
> &#x20;           \
> {\
> &#x20; "additionalData": { "offer": "summer2025", "discount": "20%" },\
> &#x20; "options": {\
> &#x20;   "updateMetadata": true,\
> &#x20;   "throughput": 12,\
> &#x20;   "correlationId": "campaign-summer-2025"\
> &#x20; }\
> }\
> \`\`\`

````json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["ScopedAuthorizeRequirement"]},{"apiKey":["ScopedAuthorizeRequirement"]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"FilterModel":{"type":"object","properties":{"field":{"type":"string","description":"Field to filter by (identifiers.*, metadata.*, passType, installationStatus)."},"operator":{"description":"Comparison operator to apply.","$ref":"#/components/schemas/FilterModelOperator"},"value":{"type":["null","array"],"items":{"type":"string"},"description":"Filter values. Single-element for most operators; multiple elements for `in` and `notIn`.\nThe query parameter name is `value` (repeated for multiple values).\n<example>\nSingle value (equals, contains, startsWith, …):\n```\nGET /passes?filter[0].field=passType&filter[0].operator=equals&filter[0].value=boarding\n```\nMultiple values (in / notIn):\n```\nGET /passes?filter[0].field=passType&filter[0].operator=in&filter[0].value=boarding&filter[0].value=loyalty\n```\nMultiple filters combined:\n```\nGET /passes?filter[0].field=passType&filter[0].operator=equals&filter[0].value=boarding\n           &filter[1].field=identifiers.customerId&filter[1].operator=startsWith&filter[1].value=CUST-\n```</example>"}},"additionalProperties":false,"description":"Filter specification for pass queries."},"FilterModelOperator":{"enum":["Contains","StartsWith","EndsWith","Equals","IsEmpty","IsNotEmpty","NotEquals","In","NotIn"],"type":"string","description":"Filter operators for pass list queries."},"MultipleUpdatePassData":{"type":"object","allOf":[{"$ref":"#/components/schemas/MultipleUpdatePassDataOptionsUpdatePassData"}],"additionalProperties":false,"description":"Data payload for multi-pass (bulk) update operations."},"MultipleUpdatePassDataOptionsUpdatePassData":{"type":"object","properties":{"identifiers":{"type":"object","additionalProperties":{"type":"string"},"description":"External identifiers of the customer for this pass. Keys must not start with `id.`; common examples are `y2.customerId` or `shopify.customerId`.\nUse an empty value to remove an identifier. Leave the collection empty to keep existing identifiers unchanged."},"additionalData":{"type":["null","object"],"additionalProperties":{"type":"null"},"description":"Arbitrary data to persist with the pass (for example, loyalty tier, store code, or campaign flags)."},"passType":{"type":["null","string"],"description":"Optional pass type to convert the pass to."},"updateMetadata":{"type":"boolean","description":"Specifies if passes metadata should be updated. Updating metadata is time consuming and could be avoided for notification only push update","default":false,"deprecated":true},"options":{"description":"Options for multi-pass (bulk) update operations.","$ref":"#/components/schemas/MultipleUpdatePassDataOptions"}},"additionalProperties":false},"MultipleUpdatePassDataOptions":{"type":"object","allOf":[{"$ref":"#/components/schemas/UpdatePassDataOptions"}],"properties":{"throughput":{"type":"number","description":"Desired throughput (passes per second) when scheduling updates. Default is 12. Throughput is best-effort and can vary. Values below 1 slowly pace updates.","format":"float"}},"additionalProperties":false,"description":"Options for multi-pass (bulk) update operations."},"UpdatePassDataOptions":{"type":"object","properties":{"updateMetadata":{"type":"boolean","description":"When true, recompute and persist pass metadata. Updating metadata is slower and is usually unnecessary for notification-only updates."},"correlationId":{"type":["null","string"],"description":"Groups related updates under the same correlationId. Useful for batch updates (for example nightly jobs) to make retries and logs traceable."}},"additionalProperties":false,"description":"Options used when updating passes."},"PushUpdateResult":{"type":"object","properties":{"passCount":{"type":"integer","description":"Count of passes scheduled for update.","format":"int32"}},"additionalProperties":false,"description":"Result returned after scheduling a bulk push update operation."}}},"paths":{"/api/{tenantId}/passes/pushUpdate":{"post":{"tags":["Pass"],"summary":"Push update for passes matching the filter.","description":"**Authorization:** Requires `Pass.Write` scope.\n\n**Filtering:** Same as GetPasses�identifiers, metadata, pass type, installation status.\n\n**Async:** Updates are queued. 200 response means scheduled, not complete. Monitor via statistics endpoint.\n\n**Data Merge:** Merged into each matching pass. Set `UpdateMetadata=true` for recomputation (slower). Adjust `Throughput` for concurrency.\n\n**Bulk Operations:** Ideal for campaigns, loyalty updates, seasonal offers. Use `CorrelationId` for tracking.\n\n**Use Cases:** Campaign push; loyalty tier changes; offer refresh; bulk metadata updates.\n\n**Example — push a seasonal offer to all loyalty passes:**\n```\nPOST /api/{tenantId}/passes/pushUpdate\n    ?filter[0].field=passType&filter[0].operator=equals&filter[0].value=loyalty\n            \n{\n  \"additionalData\": { \"offer\": \"summer2025\", \"discount\": \"20%\" },\n  \"options\": {\n    \"updateMetadata\": true,\n    \"throughput\": 12,\n    \"correlationId\": \"campaign-summer-2025\"\n  }\n}\n```","parameters":[{"name":"filter","in":"query","description":"Optional filters applied to select passes.","schema":{"type":"array","items":{"description":"Filter specification for pass queries.","$ref":"#/components/schemas/FilterModel"}}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Data to merge into each pass and options controlling metadata refresh and throughput.","content":{"application/json":{"schema":{"description":"Data payload for multi-pass (bulk) update operations.","$ref":"#/components/schemas/MultipleUpdatePassData"}},"text/json":{"schema":{"description":"Data payload for multi-pass (bulk) update operations.","$ref":"#/components/schemas/MultipleUpdatePassData"}},"application/*+json":{"schema":{"description":"Data payload for multi-pass (bulk) update operations.","$ref":"#/components/schemas/MultipleUpdatePassData"}}}},"responses":{"200":{"description":"Passes scheduled for update; returns the count in the response body.","content":{"text/plain":{"schema":{"description":"Result returned after scheduling a bulk push update operation.","$ref":"#/components/schemas/PushUpdateResult"}},"application/json":{"schema":{"description":"Result returned after scheduling a bulk push update operation.","$ref":"#/components/schemas/PushUpdateResult"}},"text/json":{"schema":{"description":"Result returned after scheduling a bulk push update operation.","$ref":"#/components/schemas/PushUpdateResult"}}}}}}}}}
````

## Cancel a running push update operation.

> \*\*Authorization:\*\* Requires \`Pass.Read\` scope.\
> \
> \*\*Cancellation:\*\* Marks operation for cancellation. May take time if updates already dispatched.\
> \
> \*\*Correlation Id:\*\* Use the id from push update schedule or statistics.\
> \
> \*\*Use Cases:\*\* Stop campaign on config error; cancel nightly batch; prevent updates on security issue.\
> \
> \*\*Example:\*\*\
> \`\`\`\
> POST /api/{tenantId}/passes/pushUpdate/campaign-summer-2025/cancel\
> \`\`\`

````json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["ScopedAuthorizeRequirement"]},{"apiKey":["ScopedAuthorizeRequirement"]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}}},"paths":{"/api/{tenantId}/passes/pushUpdate/{correlationId}/cancel":{"post":{"tags":["Pass"],"summary":"Cancel a running push update operation.","description":"**Authorization:** Requires `Pass.Read` scope.\n\n**Cancellation:** Marks operation for cancellation. May take time if updates already dispatched.\n\n**Correlation Id:** Use the id from push update schedule or statistics.\n\n**Use Cases:** Stop campaign on config error; cancel nightly batch; prevent updates on security issue.\n\n**Example:**\n```\nPOST /api/{tenantId}/passes/pushUpdate/campaign-summer-2025/cancel\n```","parameters":[{"name":"correlationId","in":"path","description":"CorrelationId used when scheduling the push update.","required":true,"schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Cancellation request accepted."}}}}}}
````

## Retrieve execution statistics for recent push update operations.

> \*\*Authorization:\*\* Requires \`Pass.Read\` scope.\
> \
> \*\*Statistics:\*\* Operation id, correlationId, start/last activity, pass count, completed count, error count, Apple/Google deployment counts, cancellation status.\
> \
> \*\*Ordering:\*\* Sorted descending by start date (most recent first).\
> \
> \*\*Use Cases:\*\* Monitor batch progress; track campaign deployment; identify failures; audit history.\
> \
> \*\*Example:\*\*\
> \`\`\`\
> GET /api/{tenantId}/passes/pushUpdate/status\
> \`\`\`

````json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["ScopedAuthorizeRequirement"]},{"apiKey":["ScopedAuthorizeRequirement"]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"PushUpdateStatistics":{"type":"object","properties":{"id":{"type":"string","description":"Unique operation identifier."},"operationName":{"type":"string","description":"Operation name/category."},"operationData":{"type":"string","description":"Correlation ID for tracking related updates."},"startDate":{"type":"string","description":"Operation start timestamp.","format":"date-time"},"lastActivity":{"type":["null","string"],"description":"Last activity timestamp.","format":"date-time"},"passCount":{"type":"integer","description":"Total passes scheduled for update.","format":"int32"},"completed":{"type":"integer","description":"Number of passes successfully updated.","format":"int32"},"errors":{"type":"integer","description":"Number of passes with update errors.","format":"int32"},"apple":{"type":"integer","description":"Number of Apple Pass updates deployed.","format":"int32"},"google":{"type":"integer","description":"Number of Google Pass updates deployed.","format":"int32"},"isCanceled":{"type":"boolean","description":"Whether the operation was cancelled."}},"additionalProperties":false,"description":"Aggregated status of a push update operation."}}},"paths":{"/api/{tenantId}/passes/pushUpdate/status":{"get":{"tags":["Pass"],"summary":"Retrieve execution statistics for recent push update operations.","description":"**Authorization:** Requires `Pass.Read` scope.\n\n**Statistics:** Operation id, correlationId, start/last activity, pass count, completed count, error count, Apple/Google deployment counts, cancellation status.\n\n**Ordering:** Sorted descending by start date (most recent first).\n\n**Use Cases:** Monitor batch progress; track campaign deployment; identify failures; audit history.\n\n**Example:**\n```\nGET /api/{tenantId}/passes/pushUpdate/status\n```","parameters":[{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Statistics retrieved successfully.","content":{"text/plain":{"schema":{"type":"array","items":{"description":"Aggregated status of a push update operation.","$ref":"#/components/schemas/PushUpdateStatistics"}}},"application/json":{"schema":{"type":"array","items":{"description":"Aggregated status of a push update operation.","$ref":"#/components/schemas/PushUpdateStatistics"}}},"text/json":{"schema":{"type":"array","items":{"description":"Aggregated status of a push update operation.","$ref":"#/components/schemas/PushUpdateStatistics"}}}}}}}}}}
````

## Get pass distribution statistics

> Returns aggregated counts of passes by installation status and platform.\
> \
> \## Authorization\
> Requires \`Pass.Read\` scope.\
> \
> \## Metrics Explained\
> \- \*\*Total\*\*: All passes created (regardless of installation)\
> \- \*\*Active\*\*: Passes currently installed on at least one device\
> \- \*\*Apple\*\*: Passes installed on Apple Wallet\
> \- \*\*Google\*\*: Passes installed on Google Wallet\
> \
> \## Filtering\
> Optional \`passType\` parameter filters statistics to a specific pass type (e.g., "loyalty", "coupon").\
> \
> \## Performance Note\
> This endpoint queries all passes and may be slow for large datasets. For production dashboards, consider using the Insights API with pre-aggregated queries.

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["ScopedAuthorizeRequirement"]},{"apiKey":["ScopedAuthorizeRequirement"]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"PassStatistic":{"type":"object","properties":{"count":{"description":"Number of active pass","$ref":"#/components/schemas/PassStatisticCount"}},"additionalProperties":false,"description":"Get statistics about pass usage"},"PassStatisticCount":{"type":"object","properties":{"total":{"type":"integer","description":"Total number of passes","format":"int32"},"active":{"type":"integer","description":"Total number of installed pass","format":"int32"},"apple":{"type":"integer","description":"Total number of active pass for apple","format":"int32"},"google":{"type":"integer","description":"Total number of active pass for google","format":"int32"}},"additionalProperties":false,"description":"Get number of active passes per device"}}},"paths":{"/api/{tenantId}/passes/stats":{"get":{"tags":["Pass"],"summary":"Get pass distribution statistics","description":"Returns aggregated counts of passes by installation status and platform.\n\n## Authorization\nRequires `Pass.Read` scope.\n\n## Metrics Explained\n- **Total**: All passes created (regardless of installation)\n- **Active**: Passes currently installed on at least one device\n- **Apple**: Passes installed on Apple Wallet\n- **Google**: Passes installed on Google Wallet\n\n## Filtering\nOptional `passType` parameter filters statistics to a specific pass type (e.g., \"loyalty\", \"coupon\").\n\n## Performance Note\nThis endpoint queries all passes and may be slow for large datasets. For production dashboards, consider using the Insights API with pre-aggregated queries.","parameters":[{"name":"passType","in":"query","description":"Optional pass type filter (must match a tenant pass configuration file).","schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"text/plain":{"schema":{"description":"Get statistics about pass usage","$ref":"#/components/schemas/PassStatistic"}},"application/json":{"schema":{"description":"Get statistics about pass usage","$ref":"#/components/schemas/PassStatistic"}},"text/json":{"schema":{"description":"Get statistics about pass usage","$ref":"#/components/schemas/PassStatistic"}}}}}}}}}
```
