> ## Documentation Index
> Fetch the complete documentation index at: https://docs.masivo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Add a coupon to a wallet

> Issue, detect, redeem, and revoke coupon wallet passes from the Storefront API.

Use these endpoints from your backend or site. The customer can also open
`claim_url` and tap the wallet buttons with no app install.

Dashboard setup (credentials, template, campaign toggle) is in
[Wallet pass credentials](https://docs.masivo.ai/guides/settings/wallet-passes.md) and
[Add coupons to Apple and Google Wallet](https://docs.masivo.ai/guides/wallet-passes.md).

> [!NOTE]
>
> Claiming assigns the code to the customer. Redeem afterwards with
> [`POST Redeem coupon`](https://docs.masivo.ai/api-reference/coupons/redeem/post).

## Prerequisites

* SERVER API key and `x-account-id`
* Coupon campaign with **Allow Add to Wallet** and a template
* Apple and/or Google credentials saved for the platforms you will issue

All examples use:

```
Authorization: Bearer <SERVER_API_KEY>
x-account-id: <ACCOUNT_ID>
Content-Type: application/json
```

## 1. Create the campaign link

[`POST /coupons/wallet-passes`](https://docs.masivo.ai/api-reference/coupons/wallet-passes/post)

One call per campaign. Repeat calls return the same URLs. Codes are assigned
when someone opens `claim_url`, not in this POST.

```bash theme={null}
curl -X POST "https://app.masivo.ai/api/storefront/v1/coupons/wallet-passes" \
  -H "Authorization: Bearer <SERVER_API_KEY>" \
  -H "x-account-id: <ACCOUNT_ID>" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_id": "3f2c8c1a-7b0e-4c2a-9d11-0c6a1b8e4f20"
  }'
```

| Body field | Required | Notes |
| - | - | - |
| `campaign_id` | Yes | Coupon campaign UUID. Brand comes from the campaign. |
| `template_id` | No | Overrides the campaign template |

Example response:

```json theme={null}
{
  "data": {
    "campaign_id": "3f2c8c1a-7b0e-4c2a-9d11-0c6a1b8e4f20",
    "claim_url": "https://app.masivo.ai/w/Efgxsi2QoE7BoqNZVhvDe5yoFkKju1rL"
  }
}
```

Share `claim_url` in email, WhatsApp, or a Masivo short link. Each new visitor
gets the next free code. The same phone keeps that code.

Read the same link later with
[`GET /coupons/wallet-passes`](https://docs.masivo.ai/api-reference/coupons/wallet-passes/get).
It does not create a link or assign a code.

```bash theme={null}
curl "https://app.masivo.ai/api/storefront/v1/coupons/wallet-passes?campaign_id=3f2c8c1a-7b0e-4c2a-9d11-0c6a1b8e4f20" \
  -H "Authorization: Bearer <SERVER_API_KEY>" \
  -H "x-account-id: <ACCOUNT_ID>"
```

You can also pass `token` from `/w/{token}` instead of `campaign_id`. The
response adds `campaign_name`, `header_text`, `description`, and
`barcode_format`.

## 2. Detect a pass the customer has not applied

Apps cannot list passes stored in the phone. Read Masivo instead.

**By customer** —
[`GET /customers/{id}/wallet-passes`](https://docs.masivo.ai/api-reference/customers/\[id]/wallet-passes/get)

```bash theme={null}
curl "https://app.masivo.ai/api/storefront/v1/customers/user_abc123/wallet-passes?status=ISSUED" \
  -H "Authorization: Bearer <SERVER_API_KEY>" \
  -H "x-account-id: <ACCOUNT_ID>"
```

`status` is optional. Allowed values: `ISSUED`, `CLAIMED`, `REDEEMED`,
`EXPIRED`, `REVOKED`.

**By claim token** (from `/w/{token}` or a deferred deep link) —
[`GET /wallet-passes/{token}`](https://docs.masivo.ai/api-reference/wallet-passes/\[token]/get)
returns the campaign `claim_url`, landing copy, and `barcode_format`.

```bash theme={null}
curl "https://app.masivo.ai/api/storefront/v1/wallet-passes/Efgxsi2QoE7BoqNZVhvDe5yoFkKju1rL" \
  -H "Authorization: Bearer <SERVER_API_KEY>" \
  -H "x-account-id: <ACCOUNT_ID>"
```

If the customer list returns an `ISSUED` pass, show “This coupon can be applied.”

## 3. Redeem the coupon

[`POST /coupons/redeem`](https://docs.masivo.ai/api-reference/coupons/redeem/post) with the same
`code` and the `customer_id`. If the pass has no customer yet, the redeem
assigns it to that customer and sends the `COUPON_WALLET_CLAIMED` webhook. The
pass moves to `REDEEMED` once the customer reaches the campaign limit per
customer, and Wallet updates.

## 4. Revoke

[`POST /wallet-passes/{id}/revoke`](https://docs.masivo.ai/api-reference/wallet-passes/\[id]/revoke/post)

```bash theme={null}
curl -X POST "https://app.masivo.ai/api/storefront/v1/wallet-passes/8d52d42c-83c8-40f4-b67d-5f203bece407/revoke" \
  -H "Authorization: Bearer <SERVER_API_KEY>" \
  -H "x-account-id: <ACCOUNT_ID>"
```

Use this when the customer is deleted or the offer must stop. The pass becomes
`REVOKED`.

## Suggested TypeScript flow

```ts theme={null}
const headers = {
  Authorization: `Bearer ${serverKey}`,
  "x-account-id": accountId,
  "Content-Type": "application/json"
};

const issued = await fetch(
  `${base}/api/storefront/v1/coupons/wallet-passes`,
  {
    method: "POST",
    headers,
    body: JSON.stringify({
      campaign_id: campaignId
    })
  }
).then(r => r.json());

// SMS, email, or in-app button
await sendLink(customerId, issued.data.claim_url);

// At checkout, redeem the code scanned from the pass. This also links the
// pass to the customer.
const code = scannedCode;
await fetch(`${base}/api/storefront/v1/coupons/redeem`, {
  method: "POST",
  headers,
  body: JSON.stringify({ customer_id: customerId, code })
});
```

## Frequently asked questions

> [accordion] Does issue redeem the coupon?
>
> No. Issue only creates the pass. Redeem is a separate call with the coupon
> code.

> [accordion] Can I send the same claim\_url more than once?
>
> Yes. One URL per campaign. Each new visitor gets the next coupon code.
> The same phone keeps the same code. Redeem still follows campaign rules.

> [accordion] Why did Add to Apple Wallet take a few seconds?
>
> The first tap builds and signs the `.pkpass` (images and certificate).
> Opening the landing page starts that work so the tap can be faster.

> [accordion] Google Wallet says I cannot save the pass
>
> In Demo mode the Gmail on the phone must be listed under **Configure test
> accounts**, and the service account email must be invited in **Users**.
> See [Wallet pass credentials](https://docs.masivo.ai/guides/settings/wallet-passes.md).

> [accordion] The pass on the phone does not match the template
>
> Delete the pass in Wallet and add it again from `claim_url`. Existing
> passes do not refresh layout until they are re-added.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.