> ## 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.

# Walletpasses

## Worked wallet-passes samples

The endpoint list below documents every operation's parameters; this
overlay walks a pass ledger the way a wallet integration actually uses
it: **issue a pass → re-check its platform payloads → update it → void
it**. Success envelopes are `{ data, meta }`, error envelopes
`{ error, meta }` — the four shapes are documented in [How to read a
worked sample](/guides/using-orbit-samples). Run samples with a sandbox
(`dv_test_sk_*`) key; nothing below leans on a provider-side wallet until
you call the install link, and the platform endpoints then return the
payloads Apple or Google Wallet consumes.

Every response carries `meta.request_id`. Quote the request id when you
report a wobble — support can trace the pass end-to-end from the ledger.

### 1. Issue a pass

`POST /api/v1/wallet-passes/issue` mints a pass into the ledger. The body
is a full `issueBodySchema` — required `type` and `title`, optional
`contact_id` / `external_id` link, colors as `#RRGGBB`, and up to 20
`fields` ({key,label,value} tuples). The default barcode is `QR_CODE`.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.orbit.devotel.io/api/v1/wallet-passes/issue" \
    -H "X-API-Key: dv_test_sk_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "type": "LOYALTY_CARD",
    "title": "Northline Rewards",
    "subtitle": "Silver tier",
    "contact_id": "cnt_8fb2d1b7c00c",
    "external_id": "order-10042",
    "fields": [{ "key": "points", "label": "Points", "value": "1,240" }],
    "background_color": "#0B2545",
    "foreground_color": "#FFFFFF"
  }'
  ```

  ```typescript Node.js theme={null}
  const res = await fetch(
    "https://api.orbit.devotel.io/api/v1/wallet-passes/issue",
    {
      method: "POST",
      headers: {
        "X-API-Key": process.env.ORBIT_API_KEY!,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        type: "LOYALTY_CARD",
        title: "Northline Rewards",
        subtitle: "Silver tier",
        contact_id: "cnt_8fb2d1b7c00c",
        external_id: "order-10042",
        fields: [
          { key: "points", label: "Points", value: "1,240" },
        ],
        background_color: "#0B2545",
        foreground_color: "#FFFFFF",
      }),
    },
  );
  console.log(await res.json());
  ```
</CodeGroup>

```json 200 theme={null}
{
  "data": {
    "id": "wp_8fb2d1b7c00c4ec9",
    "type": "LOYALTY_CARD",
    "title": "Northline Rewards",
    "subtitle": "Silver tier",
    "status": "active",
    "contact_id": "cnt_8fb2d1b7c00c",
    "external_id": "order-10042",
    "platforms": {
      "apple": { "configured": true, "pass_json": {} },
      "google": { "configured": false, "object": {}, "save_url": "https://pay.google.com/gp/v/save/…" }
    },
    "expires_at": "2027-08-01T00:00:00.000Z"
  },
  "meta": {
    "request_id": "req_wp_issue",
    "timestamp": "2026-08-26T12:00:00.000Z"
  }
}
```

`platforms.apple.configured` / `platforms.google.configured` tell you
whether your tenant's wallet credentials are in place; the payload keys
(`pass_json` / `object` / `save_url`) are what a mobile client.download
wraps into the wallet install link.

### 2. Re-read the pass

`GET /api/v1/wallet-passes/{id}` re-issues the pass record with the
platform payloads rebuilt on every read (the response below is a live
payloads snapshot, not a cached one). Prefer a re-read over an issue echo
when you need the current barcode or the latest status.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET \
    "https://api.orbit.devotel.io/api/v1/wallet-passes/wp_8fb2d1b7c00c4ec9" \
    -H "X-API-Key: dv_test_sk_YOUR_KEY"
  ```

  ```typescript Node.js theme={null}
  const res = await fetch(
    "https://api.orbit.devotel.io/api/v1/wallet-passes/wp_8fb2d1b7c00c4ec9",
    {
      headers: { "X-API-Key": process.env.ORBIT_API_KEY! },
    },
  );
  console.log(await res.json());
  ```
</CodeGroup>

### 3. Update a pass

`POST /api/v1/wallet-passes/{id}/update` is the in-place rewrite the
ledger exposes — send only the fields you are changing. The update echoes
the fresh state including `fields` and colors, and it is complete (the
guard `at least one field must be provided` rejects an empty body at 422).

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST \
    "https://api.orbit.devotel.io/api/v1/wallet-passes/wp_8fb2d1b7c00c4ec9/update" \
    -H "X-API-Key: dv_test_sk_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "subtitle": "Silver tier — 1,250 points",
    "fields": [{ "key": "points", "label": "Points", "value": "1,250" }]
  }'
  ```

  ```typescript Node.js theme={null}
  const res = await fetch(
    "https://api.orbit.devotel.io/api/v1/wallet-passes/wp_8fb2d1b7c00c4ec9/update",
    {
      method: "POST",
      headers: {
        "X-API-Key": process.env.ORBIT_API_KEY!,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        subtitle: "Silver tier — 1,250 points",
        fields: [{ key: "points", label: "Points", value: "1,250" }],
      }),
    },
  );
  console.log(await res.json());
  ```
</CodeGroup>

```json 200 theme={null}
{
  "data": {
    "id": "wp_8fb2d1b7c00c4ec9",
    "type": "LOYALTY_CARD",
    "title": "Northline Rewards",
    "subtitle": "Silver tier — 1,250 points",
    "status": "active",
    "fields": [{ "key": "points", "label": "Points", "value": "1,250" }],
    "platforms": {
      "apple": { "configured": true, "pass_json": {} },
      "google": { "configured": false, "object": {}, "save_url": "https://pay.google.com/gp/v/save/…" }
    }
  },
  "meta": {
    "request_id": "req_wp_update",
    "timestamp": "2026-08-26T12:01:00.000Z"
  }
}
```

### 4. Void a pass

`POST /api/v1/wallet-passes/{id}/void` flips the ledger to `voided` —
the pass stops validating at a scan, the reason travels with the ledger
row, and the void is idempotent-safe because the ledger keeps the original
issue rows attached.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST \
    "https://api.orbit.devotel.io/api/v1/wallet-passes/wp_8fb2d1b7c00c4ec9/void" \
    -H "X-API-Key: dv_test_sk_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "reason": "customer requested cancellation" }'
  ```

  ```typescript Node.js theme={null}
  const res = await fetch(
    "https://api.orbit.devotel.io/api/v1/wallet-passes/wp_8fb2d1b7c00c4ec9/void",
    {
      method: "POST",
      headers: {
        "X-API-Key": process.env.ORBIT_API_KEY!,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        reason: "customer requested cancellation",
      }),
    },
  );
  console.log(await res.json());
  ```
</CodeGroup>

```json 200 theme={null}
{
  "data": {
    "id": "wp_8fb2d1b7c00c4ec9",
    "status": "voided",
    "voided_reason": "customer requested cancellation"
  },
  "meta": {
    "request_id": "req_wp_void",
    "timestamp": "2026-08-26T12:02:00.000Z"
  }
}
```

### 5. Errors

Errors follow the `{ error, meta }` envelope. The failure every issuer
hits:

**422 — field validation.** Colors not in `#RRGGBB`, more than 20
`fields`, or an empty update body:

```json 422 theme={null}
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "background_color must be a #RRGGBB hex color.",
    "status": 422
  },
  "meta": {
    "request_id": "req_wp_err",
    "timestamp": "2026-08-26T12:03:00.000Z"
  }
}
```
