# On your mobile app

Make it easy for users to install Wallet passes directly from within your mobile app. Instead of redirecting users elsewhere, your app stays in control of identity, entitlements, and data  while Wallet provides instant access to passes offline, surfaces them at the right time, and updates them live. In-app installation ensures a fast, convenient experience users can present at a store or event without having to reopen your app.

<figure><img src="https://3566051324-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWLc8AHXW4tdrAXUBfrYF%2Fuploads%2FleduwIV8lWyuL8kpPvmk%2FMobileAppSDK.png?alt=media&#x26;token=0b97659c-b01b-4fe2-a005-280fb2b256d3" alt="Mobile App Add To Wallet SDK integration "><figcaption></figcaption></figure>

## Add to Wallet from your mobile app

On both Apple Wallet and Google Wallet, the flow is consistent. Your app detects whether the pass is already installed, shows the right “Add to Wallet” button, and launches the system save flow when needed.

{% hint style="info" %}
Your app cannot force installation. The user must always confirm in the system UI.
{% endhint %}

### Native app and Wallet are complementary

A wallet does not replace your native app. They solve different jobs, and they work best together. Wallet focuses on presentation and convenience. It gives quick access, offline usage, and OS-level storage. It can also surface passes at the right moment (lock screen, time, location).

Your app handles accounts, authentication, settings, and the full feature set. The Wallet Crew is where passes are created, refreshed, and revoked.

### Why include “Add to Wallet” in your app

Adding the button reduces friction at real touchpoints. Users install the pass in one tap, then present it without reopening the app. That matters when the line is long and the network is weak.

Wallet also keeps the pass reachable for months, and it can stay reachable even if the app is later uninstalled. On the analytics side, you can measure adoption and placement performance by tagging installs with `neo.src`.

<details>

<summary>Real-world examples</summary>

Here are examples of what a pass can unlock once it’s in Wallet:

* Membership: “Free shipping” on every online order.
* Retail: “10% off all purchases” during the whole season.
* Event ticketing: “Fast lane entry” available for this event.
* Event ticketing: “Seat upgrade” applied automatically when available.
* Service: “Priority support” available for every ticket submission.

</details>

### Typical end-to-end flow

The platform details differ, but the product flow stays the same. The user signs in, taps the button, and your app calls your backend. Your backend fetches the right pass from The Wallet Crew and returns an install payload that matches the device (Apple `.pkpass` or a Google JWT). The app then opens the native wallet save UI, and you track installs through Wallet Crew events.

{% @mermaid/diagram content="sequenceDiagram
autonumber
actor User
participant App as Mobile app
participant Backend as Your backend
participant TWC as The Wallet Crew backend
participant Wallet as Apple/Google Wallet (system UI)

```
User->>App: Tap “Add to Wallet”
App->>Backend: Request install payload (user context + device)

Backend->>TWC: Resolve passId (lookup by external identifier)
TWC-->>Backend: passId

Backend->>TWC: Fetch install payload (passId + device + neo.src)
TWC-->>Backend: Apple .pkpass OR Google JWT

Backend-->>App: Return install payload
App->>Wallet: Launch system save flow
Wallet-->>User: Preview + confirmation
User->>Wallet: Confirm installation

Wallet->>TWC: Register installation (provider callback)
TWC-->>Backend: (Optional) Pass:Installed event (webhook)" %}
```

{% hint style="warning" %}
Never call The Wallet Crew APIs from the mobile client with an API key. Keep secrets on your backend only.
{% endhint %}

### Apple Wallet vs Google Wallet

Your mobile app uses native wallet UI, but it never talks directly to The Wallet Crew with an API key. Your backend is the only component that calls **The Wallet Crew backend** to resolve `passId` and generate the install payload.

{% tabs %}
{% tab title="Apple Wallet (iOS)" %}
Apple Wallet uses a **signed pass bundle**.

Your backend requests an Apple install payload from The Wallet Crew backend. It returns a downloadable `.pkpass`.

Your iOS app downloads the `.pkpass` and presents it with PassKit. iOS shows the standard preview sheet, and the user confirms installation. The pass is then stored locally on the device.

Use the “Add to Apple Wallet” label.
{% endtab %}

{% tab title="Google Wallet (Android)" %}
Google Wallet uses **cloud-linked objects**.

Your backend requests a Google install payload from The Wallet Crew backend. It returns a Save payload, commonly a JWT, for the Google Wallet save flow.

Your Android app launches the Google save UI. The user confirms installation. The pass is linked to the user’s Google account, and it can sync across devices.

Use the “Add to Google Wallet” label.
{% endtab %}
{% endtabs %}

## How to implement it

Your tenant must be ready for Apple Wallet and/or Google Wallet, and you must already have a template and issuance flow. You also need a backend that your app can call, because secrets and pass lookup must stay server-side.

#### High-level architecture

At a high level, your backend resolves a stable identifier into a `passId`, then calls The Wallet Crew APIs using `X-API-KEY`. Your app only receives the provider-specific install payload and installs the pass using native wallet APIs.

#### Step-by-step

{% stepper %}
{% step %}

### Create an API key (backend only)

Create an API key in the admin console.\
Use least privilege.

See: [API Key](https://docs.thewalletcrew.io/configure/platform/api-key)

You will send it as:

`X-API-KEY: <your_api_key>`
{% endstep %}

{% step %}

### Resolve the passId on your backend

Use your own stable identifier.\
Examples: loyalty ID, customer ID, ticket ID.

Call the passes list endpoint with a filter.

Example (lookup by `identifiers.ur.customerId`):

```bash
curl --globoff -X GET \
  'https://app.neostore.cloud/api/<tenantId>/passes?pageIndex=0&pageSize=10&filter[0].field=identifiers.ur.customerId&filter[0].operator=equals&filter[0].value=<customerId>' \
  -H 'accept: application/json' \
  -H 'X-API-KEY: <your_api_key>'
```

Adapt `filter[0].field` to your own identifier key.\
Typical keys look like `identifiers.<namespace>.<name>`.

You’ll get a JSON array containing the pass `id`.\
That `id` is the `passId` you’ll use next.
{% endstep %}

{% step %}

### Generate a provider-specific “install payload”

Once you have `passId`, request the payload for the target device. Apple returns a downloadable `.pkpass` file, and Google returns a JWT token. A typical endpoint pattern looks like this:

`GET https://app.neostore.cloud/api/<tenantId>/passes/<passId>?device=<apple|google>&neo.src=<tracking>`

{% hint style="info" %}
`neo.src` is used for tracking and attribution.\
Format: `<medium>|<tag1>,<tag2>|<originUrl>`.
{% endhint %}
{% endstep %}

{% step %}

### Install the pass from the app

Use native wallet SDKs. On iOS, download the `.pkpass` and present the “Add to Apple Wallet” UI. On Android, pass the JWT to the Google Wallet save flow.

Provider docs:

{% embed url="<https://developer.apple.com/documentation/passkit/pkaddpassbutton/>" %}

{% embed url="<https://developers.google.com/wallet/retail/loyalty-cards/android>" %}
{% endstep %}

{% step %}

### Track installs and handle re-installs

Use Wallet Crew events to measure adoption and sources. In practice, you tag each in-app placement with `neo.src` (profile, checkout, onboarding) and subscribe to `Pass:Installed` events (webhook) or query adoption via Insights.
{% endstep %}
{% endstepper %}

## FAQ

<details>

<summary>Can I put the API key in the mobile app?</summary>

No. Treat API keys like passwords. Put API calls behind your backend.

</details>

<details>

<summary>Can the app call the “install payload” endpoint directly?</summary>

Yes, if you only use a `passId` and you **do not** use an API key. This is typically safe because `passId` is opaque and non-guessable.

Do **not** implement pass lookup (by customer ID) on the client.

</details>

<details>

<summary>Should I use native wallet flows or the website SDK in a mobile app?</summary>

In a mobile app, prefer **native wallet flows**. They give the best UX and the least friction. They also avoid web redirects.

Use the **website SDK** when you distribute from web pages, or when you need a desktop QR fallback.

See: [On your website](https://docs.thewalletcrew.io/enroll/on-your-website)

</details>

<details>

<summary>How should I integrate “Add to Wallet” in a Flutter app?</summary>

This pattern works well in Flutter. Keep pass lookup and payload generation on your backend, then trigger the native iOS/Android wallet save flows from Flutter.

</details>

<details>

<summary>How should I integrate “Add to Wallet” in a React Native app?</summary>

Use native modules or a maintained wallet library, then launch the native save flow on each platform. A practical starting point is: <https://habr.com/en/articles/858858/>

</details>

<details>

<summary>Apple vs Google: what do I receive?</summary>

Apple Wallet returns a `.pkpass` file. Google Wallet returns a JWT. Your backend should pick the right payload per device.

</details>

<details>

<summary>What if the pass does not exist yet?</summary>

Create it first, then re-run the same flow to fetch the `passId` and install payload.

Start here: [How to Create Passes via API in The Wallet Crew](https://docs.thewalletcrew.io/develop/guides/wallet/how-to-create-passes-via-api-in-the-wallet-crew)

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.thewalletcrew.io/enroll/on-your-mobile-app.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
