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

# Bulk cards

> Issue and update multiple wallet cards in one integration flow

Use this guide when your system enrolls or updates many cardholders at once, for example a nightly sync from your membership database or a roster import after an event.

<Note>
  For one-time migration of thousands of existing members, Bambu also supports **CSV upload** in the admin portal (Distribution → Upload CSV). Use the API flows below for ongoing batch operations from your own systems.
</Note>

## When to use which endpoint

| Goal                                               | Endpoint                                                     | Best for                                         |
| -------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------ |
| **Create** multiple new cards                      | `POST /{brand_id}/issue-wallets`                             | Batch enrollment; each item is one cardholder    |
| **Apply the same change** to many existing cards   | `PATCH /{brand_id}/programs/{program_id}/bulk-wallets`       | Campaign message, tier move, shared field update |
| **Update one card** with different data per member | `PATCH /{brand_id}/programs/{program_id}/wallets?unique_id=` | Loop in your job, one request per `unique_id`    |
| **Schedule** a bulk change for later               | `PATCH .../bulk-wallets/schedule`                            | Time-zone-aware rollouts                         |

## Prerequisites

1. [Authenticate](/guides/authentication) and obtain a Bearer token.
2. Know your `brand_id`, `program_id`, and unique field name (for example `memberId` in `passdata.metaData`).
3. See [Unique identifier](/guides/unique-identifier) for how `unique_id` ties create and update together.

***

## Scenario: enroll a batch of new members

Your registration system has a list of new members to receive wallet passes. Send them in one `issue-wallets` request.

### Request

```http theme={null}
POST /{brand_id}/issue-wallets
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
```

```json theme={null}
{
  "groupId": "roster-2026-07-28",
  "passes": [
    {
      "person": {
        "firstName": "Alex",
        "lastName": "Member",
        "email": "alex@example.com",
        "phone": "+15551234567"
      },
      "passdata": {
        "points": 0,
        "metaData": { "memberId": "1001" }
      }
    },
    {
      "person": {
        "firstName": "Jordan",
        "lastName": "Player",
        "email": "jordan@example.com"
      },
      "passdata": {
        "points": 0,
        "metaData": { "memberId": "1002" }
      }
    }
  ]
}
```

| Field      | Notes                                                                                                                  |
| ---------- | ---------------------------------------------------------------------------------------------------------------------- |
| `groupId`  | Optional. Groups issued passes for reporting. Use a batch or job ID from your system.                                  |
| `passes[]` | Same shape as a single [issue-wallet](/guides/getting-started) body. Each card needs a distinct `metaData` unique key. |

### Response

Save `passId`, `downloadUrlApple`, and `downloadUrlGoogle` per member from the response. Distribute install links through your own email or SMS channel.

### Tips

* Keep batches at a size your job runner can retry (ask Bambu about rate limits for your tenant).
* If one member already exists, behavior depends on brand rules. Design your job to log failures and continue.
* Do not call `POST /people` first; each pass entry creates the cardholder when needed.

***

## Scenario: update many cards with the same change

Your loyalty program needs to push the same message or points rule to every card in a tier, for example all **Active** members before an event.

### Request

Use query parameters to **filter** which passes receive the change, and the body for **what** changes.

```http theme={null}
PATCH /{brand_id}/programs/{program_id}/bulk-wallets?template_tier_id=2
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
```

```json theme={null}
{
  "currentMessage": "Season opener this Saturday. See you there!",
  "passdata": {
    "points": 50
  }
}
```

| Query param        | Purpose                                                   |
| ------------------ | --------------------------------------------------------- |
| `template_tier_id` | Limit to cards in a specific stage/tier                   |
| `template_id`      | Limit to a template                                       |
| `unique_id`        | Limit to one card (usually use single-card PATCH instead) |
| `pass_ids`         | Limit to an explicit list of pass IDs                     |

The response includes an `updateId` you can track:

```json theme={null}
{
  "updateId": "bulk-update-abc123"
}
```

Check job status with `GET /{brand_id}/programs/{program_id}/bulk-wallets`.

### Tips

* Test filters on a small segment in sandbox before running against production.
* Bulk update applies the same payload to all matching cards. It is not for per-member field differences.
* For different data per member, loop `PATCH .../wallets?unique_id=` instead.

***

## Scenario: different updates per member in one job

When each cardholder needs different field values (names, points, metadata), run a server-side job that calls the single-card endpoint once per member:

```http theme={null}
PATCH /{brand_id}/programs/{program_id}/wallets?unique_id=1001
```

```json theme={null}
{
  "passdata": {
    "points": 120,
    "metaData": { "memberId": "1001" }
  }
}
```

Repeat for `unique_id=1002`, `1003`, and so on. This pattern is simple to retry per row and maps cleanly to CSV or database exports.

***

## End-to-end flow

```mermaid theme={null}
sequenceDiagram
    participant Job as Your batch job
    participant Auth as OAuth token URL
    participant API as Brand API

    Job->>Auth: POST client_credentials
    Auth-->>Job: access_token

    alt New members
        Job->>API: POST /issue-wallets
        API-->>Job: passIds + download URLs
    else Same change for many cards
        Job->>API: PATCH /bulk-wallets?template_tier_id=
        API-->>Job: updateId
        Job->>API: GET /bulk-wallets
        API-->>Job: job status
    else Different data per member
        loop Each unique_id
            Job->>API: PATCH /wallets?unique_id=
            API-->>Job: updated pass
        end
    end
```

## Related reference

* **API Reference → Authentication**: same OAuth flow, linked from the endpoint playground
* **API Reference → Wallet cards →** `POST /{brand_id}/issue-wallets`
* **API Reference → Bulk & scheduled updates →** `PATCH /{brand_id}/programs/{program_id}/bulk-wallets`
