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

# GET /v1/loyalty/memberships/{id}

> Get one membership with its balance, stamp count, tier, and wallet serial number.

Returns a single membership — the current state of one customer inside one program. This is the balance you show at the till.

## Endpoint

```
GET https://api.keebai.com/v1/loyalty/memberships/{id}
```

## Required scope

`loyalty:read`

## Headers

| Header          | Required | Value                    |
| --------------- | -------- | ------------------------ |
| `Authorization` | Yes      | `Bearer kbai_pk_<token>` |

## Path parameters

| Parameter | Type     | Description                |
| --------- | -------- | -------------------------- |
| `id`      | `string` | The membership `ObjectId`. |

## Example request

<CodeGroup>
  ```bash curl theme={null}
  curl https://api.keebai.com/v1/loyalty/memberships/6650aaaa1111bbbb2222cccc \
    -H "Authorization: Bearer kbai_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  ```

  ```js JavaScript theme={null}
  const response = await fetch(
    "https://api.keebai.com/v1/loyalty/memberships/6650aaaa1111bbbb2222cccc",
    { headers: { Authorization: `Bearer ${process.env.KEEBAI_API_TOKEN}` } },
  );
  const { membership } = await response.json();
  console.log(`${membership.balance_points} points`);
  ```

  ```python Python theme={null}
  import os, requests

  resp = requests.get(
      "https://api.keebai.com/v1/loyalty/memberships/6650aaaa1111bbbb2222cccc",
      headers={"Authorization": f"Bearer {os.environ['KEEBAI_API_TOKEN']}"},
      timeout=10,
  )
  resp.raise_for_status()
  membership = resp.json()["membership"]
  ```
</CodeGroup>

## Response

### 200 OK

```json theme={null}
{
  "membership": {
    "_id": "6650aaaa1111bbbb2222cccc",
    "program_id": "6650a1b2c3d4e5f6a7b8c9d0",
    "customer": {
      "id": "pos-8842",
      "name": "Camila Rojas",
      "phone": "+56912345678"
    },
    "serial_number": "c0e9561b-4c2f-4f0a-9c1e-8b7d6a5f4e3d",
    "balance_points": 1240,
    "stamps_count": 0,
    "tier": "silver",
    "status": "active",
    "enrolled_at": "2026-03-01T00:00:00.000Z",
    "created_at": "2026-03-01T00:00:00.000Z",
    "updated_at": "2026-06-18T11:42:03.550Z"
  }
}
```

### 401 Unauthorized

Missing, invalid, revoked, or expired token.

### 403 Forbidden

The token does not have the `loyalty:read` scope.

### 404 Not Found

No membership with that id in your company. A malformed id returns `404` as well, not `400`.

### 502 Bad Gateway

The loyalty service is unreachable.

## Operational notes

* **`balance_points` and `stamps_count` both exist regardless of program type.** Only the one the program uses moves; the other stays at `0`. Read the program's `type` to know which to show.
* **`serial_number` is the wallet identity.** It is what the pass is keyed by in Apple and Google, and what the card's barcode encodes by default. Scanning a customer's card gives you this value, and you can resolve it back through this endpoint's list counterpart.
* **`status` is set in the portal, not here.** A `suspended` or `revoked` membership still accepts ledger movements through the API — the status is a label your own flow should honour, not an enforced lock.
* **The point-expiry caveat applies.** On a program with `expiration_days`, this can report `balance_points: 0` while the ledger still shows a positive `balance_after`.


## OpenAPI

````yaml GET /v1/loyalty/memberships/{id}
openapi: 3.0.0
info:
  title: Keebai Public API
  description: Superficie pública REST autenticada con Personal Access Tokens (PAT).
  version: 1.0.0
  contact: {}
servers:
  - url: https://api.keebai.com
    description: Production
security: []
tags: []
paths:
  /v1/loyalty/memberships/{id}:
    get:
      tags:
        - loyalty
      summary: Obtener una membresía
      operationId: PublicLoyaltyController_getMembership
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicMembershipEnvelopeDto'
      security:
        - PAT: []
components:
  schemas:
    PublicMembershipEnvelopeDto:
      type: object
      properties:
        membership:
          $ref: '#/components/schemas/PublicMembershipDto'
      required:
        - membership
    PublicMembershipDto:
      type: object
      properties:
        _id:
          type: string
        program_id:
          type: string
        customer:
          $ref: '#/components/schemas/PublicMembershipCustomerViewDto'
        serial_number:
          type: string
          description: >-
            Identificador del pase en Apple y Google. Es lo que codifica el
            código de barras por defecto
        balance_points:
          type: number
          description: >-
            Saldo de puntos. Si el programa vence puntos y ya pasó el plazo, se
            lee 0 sin que el ledger cambie
          example: 1240
        stamps_count:
          type: number
          example: 7
        tier:
          type: string
          description: Clave del nivel alcanzado. Vacío si el programa no es de tiers
          example: gold
        status:
          type: string
          enum:
            - active
            - suspended
            - revoked
        enrolled_at:
          type: string
        created_at:
          type: string
        updated_at:
          type: string
      required:
        - _id
        - program_id
        - serial_number
        - balance_points
        - stamps_count
        - tier
        - status
        - enrolled_at
        - created_at
        - updated_at
    PublicMembershipCustomerViewDto:
      type: object
      properties:
        id:
          type: string
          example: crm-8842
        name:
          type: string
          example: Camila Rojas
        phone:
          type: string
          example: '+56912345678'
        email:
          type: string
          example: camila@ejemplo.cl
      required:
        - id
  securitySchemes:
    PAT:
      scheme: bearer
      bearerFormat: kbai_pk_<hex>
      type: http
      description: >-
        Personal Access Token con prefijo `kbai_pk_`. Generar desde el portal
        con permiso `developer.manage_tokens`.

````