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

# Wallet Passes console reference

> The /marketing/wallet-passes console end to end: the issued-passes table and the generation counter, the issue and update dialogs, the Void action, the scope gates on reads and writes, and the API calls behind each control.

# Wallet Passes

The Wallet Passes console at `/marketing/wallet-passes` manages the artifact your members carry in Apple Wallet and Google Wallet: loyalty cards, coupons, and event tickets. The console drives the `issue → update → void` lifecycle, and the generation counter on each pass is the refresh signal to holder devices. The [marketing hub overview](/marketing/overview) positions this console against the other three; the [Wallet passes guide](/guides/wallet-passes) runs the issue → deliver → update → void pipeline end to end; the [concept page](/concepts/wallet-pass-lifecycle) defines the lifecycle underneath.

Wallet Passes is a tenant-owned control. Passes carry your brand, your fields, and your barcodes; the platform issues and refreshes exactly what you define.

## What you see on the page

**Issued passes.** One table, newest first:

| Column | What it tells you |
| - | - |
| **Recipient** | The contact the pass was issued to, or the external reference when issued by reference |
| **Pass** | The pass title with the type underneath — loyalty card, coupon, or ticket |
| **Status** | The lifecycle state, badged; `active` passes can still be updated and voided |
| **Generation** | How many times the pass content has been revised. Every update bumps it, and holder devices refresh on the bump without a re-save |
| **Issued** | When the pass was minted |
| **Update** / **Void** | The lifecycle actions, enabled only while the row is `active` |

The table pages 25 rows at a time with **Previous** and **Next**, and a **Contact ID** filter with **Apply** narrows it to one member.

**Issue pass.** The header button opens the issue dialog:

* **Pass type** — loyalty card, coupon, or ticket
* **Contact ID** or **External ref** — who carries the pass, or your own order or ticket id
* **Title** and optional **Subtitle** — what the holder reads first
* **Organization name** and **Description** — your brand on the pass
* **Barcode message** — the scanned value at your counter, for example the member code

**Update pass.** The row's **Update** opens a dialog over the same content — title, subtitle, description, barcode message — and saving it bumps the row's **Generation**.

## Who can read and who can act

The dashboard gates the console at the workspace level; the API splits reads from writes at the scope level:

| Surface | Read | Write |
| - | - | - |
| Dashboard console | Workspace roles **owner**, **admin**, or **developer** | The same roles |
| Wallet Passes API | Key with the `contacts:read` scope | Key with the `contacts:write` scope |

A credential with only `contacts:read` can list passes and receives a `403` on issue, update, and void.

## Worked examples

### Issue a loyalty card to a member

1. Choose **Issue pass**.
2. Set the pass type to loyalty card, the member's **Contact ID**, your **Organization name**, and the member's code as **Barcode message**.
3. Confirm, then deliver the pass link over any channel you already use. The pass installs into the holder's wallet.

### Bump the generation counter

1. Find the member's active pass and choose **Update**.
2. Change what moved — a new tier in the title, a refreshed barcode, a corrected description.
3. Save. The row's **Generation** goes up by one, and every device holding that pass refreshes its content without the member re-saving anything. That is the whole refresh contract: content change plus generation bump.

### Void a pass when the membership ends

1. Choose **Void** on the row. The console asks for confirmation, since the action cannot be undone.
2. The status leaves `active`, and **Update** and **Void** disable on the row, on the dashboard and in the API alike.

## API pairing

The issue dialog writes `POST /api/v1/wallet-passes/issue`:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.orbit.devotel.io/api/v1/wallet-passes/issue \
    -H "X-API-Key: dv_live_sk_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "type": "loyalty_card",
      "contact_id": "cnt_member_9x2",
      "title": "Club Card",
      "organization_name": "Your Brand",
      "barcode_message": "ORBIT-CNT-9X2",
      "barcode_format": "QR_CODE",
      "background_color": "#1A1A2E",
      "fields": [
        { "key": "points", "label": "Points", "value": "0" }
      ],
      "idempotency_key": "issue-cnt_member_9x2"
    }'
  ```
</CodeGroup>

Update and void map to `POST /api/v1/wallet-passes/:id/update` and `POST /api/v1/wallet-passes/:id/void`, the same calls the row buttons make; the update response reports the new generation. Loyalty writes the pass from the other side: when a member's balance changes, the linked pass refreshes through the same generation bump.

## Tenant-owned controls

Pass types, branding, fields, barcodes, and every update and void are values you set. The platform issues and pushes what you define; it never mints a pass, bumps a generation, or voids one on your behalf.


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