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

# Find a customer

> Resolve a single BrandWallet customer by id, phone, email, or card barcode. Returns loyalty balances, wallet cards with their campaign configuration, and active coupons in one call.

```http theme={null}
GET $BASE_URL/v1/customers/find?q=<term>
```

**Resolves a customer of your organization using a single search term. Use it to identify the customer who is checking out, and to load their balances, cards, and coupons before running a transaction.**

> **Auth:** `Authorization: Bearer <accessToken>` — an organization-scoped token from [Authentication](/api/authentication). The search runs within your token's organization only.

### Query parameters

| Parameter | Type | Description |
| - | - | - |
| `q` | string | Required search term, matched in this order: **customer id** (exact), **opaque id** (exact), **phone** (case-insensitive substring), **email** (exact). When no customer field matches and the term is purely numeric, a card **barcode** (exact) is tried last. |

> A missing/empty `q` returns `_q_required` (400). No match returns `customer_not_found` (400) — match the exact string. Use URL encoding (e.g. `URLSearchParams`) so the `+` in phone numbers is not lost.

### Example

```bash theme={null}
curl --request GET \
  --url "$BASE_URL/v1/customers/find?q=%2B905551234567" \
  --header 'Authorization: Bearer <accessToken>'
```

### Response

Returns **200** with a single customer object (no envelope). The model has two layers: **the customer's state** (balances, tier level, counters) lives in the top-level `collectables`; **the rules** (earn/spend/tier configuration) live in `cards[].campaigns[].actions[].options`.

```json theme={null}
{
  "id": "9c2f1a34-6b8e-4f02-a1d3-5e7c90b4d812",
  "data": {
    "fullname": "Jane Doe",
    "phone": "+905551234567",
    "email": "jane@example.com"
  },
  "collectables": {
    "point_A1B2C": {
      "label": "Points",
      "value": 120,
      "total": 350,
      "tierLevel": 1,
      "tierStartedAt": "2026-09-01T09:12:40.000Z",
      "totalAmount": 3200
    },
    "stamp_K3M9A": { "label": "Stamps", "value": 4, "total": 12 }
  },
  "metaData": {},
  "consents": { "marketing": true },
  "tags": [
    { "id": "f31c7e2a-6d15-4a02-b7c9-0d84f2ae6b31", "name": "VIP", "color": "#f5a623" }
  ],
  "categories": [],
  "cards": [
    {
      "id": "6f1d2c88-9a41-4b7e-8d02-3c5a71e9f604",
      "name": "Point Card",
      "barcode": "483920",
      "status": 1,
      "campaigns": [
        {
          "id": "14c5a0ed-537e-400c-8463-e72bf277f600",
          "name": "Points Campaign",
          "actions": [
            {
              "name": "collect-tiered-points",
              "options": {
                "collectable": "point_A1B2C",
                "track": "totalAmount",
                "tiers": [
                  {
                    "level": 1,
                    "threshold": 0,
                    "collect": { "method": "percent", "percent": { "ratio": 5 } },
                    "spend": { "pointstoconvert": 100, "convertto": 10 }
                  }
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "id": "9c01d7c4-52aa-4f0e-8d40-2c4f27f81f02",
      "name": "Stamp Card",
      "barcode": "483925",
      "status": 1,
      "campaigns": [
        {
          "id": "8b7d1f22-90ce-4f6d-9f5b-b0a4a2f7d356",
          "name": "Stamps Campaign",
          "actions": [
            {
              "name": "collect-stamps",
              "options": {
                "collectable": "stamp_K3M9A",
                "track": "total",
                "tiers": [
                  {
                    "level": 1,
                    "collect": { "collect": "stamps", "method": "value", "value": 1 },
                    "convert": {
                      "threshold": 10,
                      "rewards": [
                        {
                          "coupon": { "id": "074308ff-44cc-44e3-987c-fd79a9301fb2", "name": "Free Coffee" },
                          "expirationoptions": { "unit": "days", "value": "90" }
                        }
                      ]
                    }
                  }
                ]
              }
            }
          ]
        }
      ]
    }
  ],
  "opaqueId": null,
  "status": 1,
  "createdAt": "2026-08-25T14:03:11.000Z",
  "updatedAt": "2026-08-27T09:12:40.000Z",
  "coupons": [
    {
      "id": "2b7e4f19-8c3d-4a56-b012-9e6f83d1c470",
      "coupon": { "name": "Welcome Coupon" },
      "code": "WELCOME10",
      "type": "discount",
      "discountAmount": 10,
      "isPercent": true,
      "issuedAt": "2026-08-20T10:00:00.000Z",
      "expiresAt": "2026-09-20T10:00:00.000Z",
      "status": "active"
    }
  ]
}
```

Notes:

* A barcode search returns only that card; phone/email searches return all of the customer's cards. `collectables` and `coupons` are scoped to the returned card(s).
* **`cards[].campaigns[]`** are the campaigns attached to the card: `{id, name?, actions}`. `actions[].options` is the campaign's configuration and passes through **as-is** — its content depends on the action type (`collect-points`, `collect-tiered-points`, `collect-stamps`, `apply-discount`, ...). Two exceptions: `options.collectable` is always the collectable **key** (matching the keys in the top-level `collectables`), and reward coupons are reduced to `{id, name}`. Inactive actions are not listed. On tiered campaigns `tiers[]` contains ALL of the campaign's levels — the example shows a single level for brevity.
* **The customer's state lives in the top-level `collectables`:** `value` is the spendable balance, `total` the lifetime collected amount. On tiered campaigns, `tierLevel` (the customer's current level) and `tierStartedAt` are also present, along with the counters the campaigns track (the key named by `options.track` — e.g. `totalAmount`). These fields are omitted when not applicable.
* **Derived values are not returned — compute them from config + state:**
  * Active tier config: the `tiers[]` entry where `level === collectables[key].tierLevel`.
  * Remaining to next tier: `nextTier.threshold - collectables[key][options.track]`.
  * Money value of one point: active tier's `spend.convertto / spend.pointstoconvert`.
  * Stamp target: active tier's `convert.threshold` — display progress as `value`/`threshold` (e.g. 4/10).
* `collect` configurations are estimates: active promotions can change the final earned amount.
* `tags` / `categories` are the labels attached to the customer (`{id, name, color?}`); `color` is omitted when not set, and both are empty arrays when the customer has none.
* `coupons` are the customer's redeemable coupons: redeemed coupons are excluded; coupons sent directly to the customer (not tied to a campaign) are always listed. Same item shape as [GET /v1/customers/:id/coupons](/api/list-coupons).
* Optional coupon fields (`maxDiscountAmount`, `includesModifiers`, `products`, `issued`, `issuedAt`, `expiresAt`, `redeemed`, `redeemedAt`) are omitted when absent — you will not receive `null`. `type: "product"` (free product) coupons come as `discountAmount: 100, isPercent: true`; the covered products are in `products.productIds` (your external product codes).
* `cards[].id` is the `card` parameter of the coupon endpoints and of [POST /v1/transactions/process](/api/process-transaction); `coupons[].id` is the `:couponId` of [redeem](/api/redeem-coupon).


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