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

# Retrieve a customer

> Retrieves a single customer by ID.

The response includes:

- **Core identity** - `first_name`, `last_name`, `email`, `phone`, `language`
- **Wallet URLs** - `claim_url`, `apple_wallet_url`, `google_wallet_url`
- **Card enrollment** - single `card` object (not an array)
- **Balances** - per card type: stamps, points, discount tier, gift card balance
- **Extensions** - `custom` schema-validated fields + `metadata` free-form



## OpenAPI

````yaml /openapi.json get /v1/customers/{id}
openapi: 3.1.0
info:
  title: VEXiON cards Public API
  description: >-
    ## Rate limiting


    Two independent layers, checked in this order:


    1. **Per-IP throttle (pre-authentication).** Applied to every `/v1` request
    BEFORE the API key is checked, keyed on the caller's IP address:
       - **600 requests/minute** per IP - a flood ceiling, `429 rate_limit_error` / `rate_limited`.
       - **20 failed authentications per 15 minutes** per IP - a key-guessing ceiling. Only responses that are `401` (missing/invalid/revoked key) count against this ceiling; a `403` from a valid key that simply lacks a scope does not. `429 rate_limit_error` / `too_many_failed_attempts`.

       Both ceilings send `Retry-After` (seconds).
    2. **Per-key, per-bucket limiter (post-authentication).** Documented per
    endpoint (see each operation's `429` response and the `rate_limit` field on
    the API Key resource) - e.g. 60/min default, 120/min for transactions, plus
    the notification-broadcast and lookup-filter buckets described on their own
    endpoints.


    ### Rate-limit response headers


    Every rate-limited response carries the legacy `X-RateLimit-Limit` /
    `X-RateLimit-Remaining` / `X-RateLimit-Reset` family (the last is an **epoch
    timestamp**) AND the IETF standards-track headers, side by side:


    - `RateLimit-Policy: "<bucket>";q=<quota>;w=<window seconds>` - the static
    policy for the bucket that was hit.

    - `RateLimit: "<bucket>";r=<remaining>;t=<seconds until reset>` - live
    state. **`t` is a DURATION (seconds remaining), not the epoch timestamp
    `X-RateLimit-Reset` carries - the two are never the same value.**
  version: 1.0.0
servers:
  - url: https://api.vexioncards.one
    description: Production
  - url: https://migration.api.vexioncards.one
    description: Sandbox
security:
  - apiKey: []
tags:
  - name: Customers
  - name: Cards
  - name: Transactions
  - name: Notifications
  - name: Locations
  - name: Products
  - name: API keys
  - name: Organization
  - name: Webhook endpoints
  - name: Events
  - name: Custom fields
  - name: Team members
  - name: Icons
paths:
  /v1/customers/{id}:
    get:
      tags:
        - Customers
      summary: Retrieve a customer
      description: >-
        Retrieves a single customer by ID.


        The response includes:


        - **Core identity** - `first_name`, `last_name`, `email`, `phone`,
        `language`

        - **Wallet URLs** - `claim_url`, `apple_wallet_url`, `google_wallet_url`

        - **Card enrollment** - single `card` object (not an array)

        - **Balances** - per card type: stamps, points, discount tier, gift card
        balance

        - **Extensions** - `custom` schema-validated fields + `metadata`
        free-form
      operationId: getCustomer
      parameters:
        - name: id
          in: path
          description: Customer id with cus_ prefix.
          required: true
          example: cus_b8c2e0a4f59d4a73a1e9
          schema:
            type: string
            pattern: ^cus_[A-Za-z0-9]+$
      responses:
        '200':
          description: Customer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Customer'
              examples:
                Reward customer installed:
                  summary: Reward customer installed
                  value:
                    id: cus_yovo5wk634htfojgkzdpmzvu5a
                    object: customer
                    external_id: crm-customer-7421
                    email: thomas.wright@example.com
                    phone: null
                    first_name: Tomáš
                    last_name: Varga
                    date_of_birth: '1991-04-12'
                    language: sk
                    marketing_opt_in: true
                    gdpr_redacted_at: null
                    card_id: crd_2hh2fekk5zazfyyvian4uhepyq
                    card_type: reward
                    card:
                      card_id: crd_2hh2fekk5zazfyyvian4uhepyq
                      card_type: reward
                      status: installed
                      total_visits: 12
                      first_transaction_at: 1779580800
                      last_transaction_at: 1782345600
                      created_at: 1779494400
                      updated_at: 1782345600
                      points: 480
                      points_pending: 0
                      reward_tier:
                        tier_name: Silver
                        earn_multiplier: 1.5
                        tier_effective_date: 1781481600
                        previous_tier_name: Bronze
                    signup_location_id: loc_ibjeubrxvvruf6aq5jfnhrz6fa
                    status: installed
                    device: apple_wallet
                    installed_at: 1779494400
                    apple_wallet_url: >-
                      https://api.vexioncards.one/api/passkit/download/afbd9cbc8b9ab72f43bf6ffc?t=dl_9f2c4e7a1b
                    google_wallet_url: null
                    claim_url: https://vexioncards.one/card/add/afbd9cbc8b9ab72f43bf6ffc
                    serial_number: DEMO-RW-0000412
                    serial_number_locked: true
                    custom: {}
                    metadata:
                      crm_secondary_id: marketing_456
                    last_delivery: null
                    referral_stats: null
                    created_at: 1779494400
                    updated_at: 1782345600
                    last_visited_at: 1782345600
                    last_visited_location_id: loc_ibjeubrxvvruf6aq5jfnhrz6fa
                    deleted_at: null
                Custom card with custom block:
                  summary: Custom card with custom block
                  value:
                    id: cus_d31a90e6b8c47fb251f0
                    object: customer
                    external_id: null
                    email: lucy.morgan@example.com
                    phone: '+421948555200'
                    first_name: Lucia
                    last_name: null
                    date_of_birth: null
                    language: sk
                    marketing_opt_in: false
                    gdpr_redacted_at: null
                    card_id: crd_zqpuluiwik4cs2aulxk3e54haa
                    card_type: custom
                    card:
                      card_id: crd_zqpuluiwik4cs2aulxk3e54haa
                      card_type: custom
                      status: installed
                      total_visits: 0
                      first_transaction_at: null
                      last_transaction_at: null
                      created_at: 1782604900
                      updated_at: 1782651200
                    signup_location_id: null
                    status: installed
                    device: google_wallet
                    installed_at: 1782604900
                    apple_wallet_url: null
                    google_wallet_url: https://pay.google.com/gp/v/save/eyJhbGciOi...
                    claim_url: https://vexioncards.one/card/add/84ad785b092fd348211c8dcc
                    serial_number: PET-0000247
                    serial_number_locked: true
                    custom:
                      pet_name: Rex
                      pet_birthday: '2022-06-27'
                      pet_species: dog
                      favourite_treat: matcha biscuit
                    metadata: {}
                    last_delivery:
                      channel: email
                      status: sent
                      to: lucy.morgan@example.com
                      sent_at: 1782604901
                    referral_stats: null
                    created_at: 1782604900
                    updated_at: 1782651200
                    last_visited_at: null
                    last_visited_location_id: null
                    deleted_at: null
          headers: {}
        '400':
          description: Malformed body or missing required field.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  type: invalid_request_error
                  code: invalid_query_parameter
                  message: Invalid value for a query parameter.
                  doc_url: >-
                    https://vexioncards.mintlify.site/api-reference/errors#invalid_query_parameter
                  request_id: req_4d8b1e2f9c3a5
              examples:
                Http 400 default:
                  summary: Http 400 default
                  value:
                    error:
                      type: invalid_request_error
                      code: invalid_query_parameter
                      message: Invalid value for a query parameter.
                      doc_url: >-
                        https://vexioncards.mintlify.site/api-reference/errors#invalid_query_parameter
                      request_id: req_4d8b1e2f9c3a5
          headers: {}
        '401':
          description: Missing, invalid, revoked, or paused API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  type: authentication_error
                  code: missing_api_key
                  message: >-
                    Authorization header is required. Send 'Authorization:
                    Bearer vxn_sk_...'.
                  doc_url: >-
                    https://vexioncards.mintlify.site/api-reference/errors#missing_api_key
                  request_id: req_01jBhZF87P8Q9R0S1T2V3W4X5Y
              examples:
                No API key:
                  summary: No API key
                  value:
                    error:
                      type: authentication_error
                      code: missing_api_key
                      message: >-
                        Authorization header is required. Send 'Authorization:
                        Bearer vxn_sk_...'.
                      doc_url: >-
                        https://vexioncards.mintlify.site/api-reference/errors#missing_api_key
                      request_id: req_01jBhZF87P8Q9R0S1T2V3W4X5Y
                API key revoked:
                  summary: API key revoked
                  value:
                    error:
                      type: authentication_error
                      code: api_key_revoked
                      message: >-
                        This API key has been revoked. Mint a new key from the
                        dashboard at
                        https://app.vexioncards.one/settings/developers.
                      doc_url: >-
                        https://vexioncards.mintlify.site/api-reference/errors#api_key_revoked
                      request_id: req_NW7d269sCXQdKKgbbQ88Hp5e40
                API key paused:
                  summary: API key paused
                  value:
                    error:
                      type: authentication_error
                      code: api_key_paused
                      message: This API key is paused. Resume it from the dashboard.
                      doc_url: >-
                        https://vexioncards.mintlify.site/api-reference/errors#api_key_paused
                      request_id: req_s6PCyHgtMN9fvdxe787mgq69QS
          headers: {}
        '403':
          description: API key lacks the required scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  type: permission_error
                  code: insufficient_scope
                  message: This API key does not have the customers:read scope.
                  doc_url: >-
                    https://vexioncards.mintlify.site/api-reference/errors#insufficient_scope
                  request_id: req_01jBhZFK8Q9R0S1T2V3W4X5Y6Z
              examples:
                Insufficient scope:
                  summary: Insufficient scope
                  value:
                    error:
                      type: permission_error
                      code: insufficient_scope
                      message: This API key does not have the customers:read scope.
                      doc_url: >-
                        https://vexioncards.mintlify.site/api-reference/errors#insufficient_scope
                      request_id: req_01jBhZFK8Q9R0S1T2V3W4X5Y6Z
          headers: {}
        '404':
          description: Resource not found in your organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  type: not_found_error
                  code: resource_missing
                  message: >-
                    Customer 'cus_does_not_exist' was not found in this
                    organization.
                  param: id
                  doc_url: >-
                    https://vexioncards.mintlify.site/api-reference/errors#resource_missing
                  request_id: req_01jBhZG09R0S1T2V3W4X5Y6Z7A
              examples:
                Customer not found:
                  summary: Customer not found
                  value:
                    error:
                      type: not_found_error
                      code: resource_missing
                      message: >-
                        Customer 'cus_does_not_exist' was not found in this
                        organization.
                      param: id
                      doc_url: >-
                        https://vexioncards.mintlify.site/api-reference/errors#resource_missing
                      request_id: req_01jBhZG09R0S1T2V3W4X5Y6Z7A
          headers: {}
        '429':
          description: Per-minute or per-day rate limit exceeded. Respect Retry-After.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  type: rate_limit_error
                  code: rate_limited
                  message: Too many requests. Try again in 5 seconds.
                  retry_after: 5
                  doc_url: >-
                    https://vexioncards.mintlify.site/api-reference/errors#rate_limited
                  request_id: req_2c8e4f6a9d1b3
              examples:
                Http 429 default:
                  summary: Http 429 default
                  value:
                    error:
                      type: rate_limit_error
                      code: rate_limited
                      message: Too many requests. Try again in 5 seconds.
                      retry_after: 5
                      doc_url: >-
                        https://vexioncards.mintlify.site/api-reference/errors#rate_limited
                      request_id: req_2c8e4f6a9d1b3
          headers: {}
        '500':
          description: Server error. Retry-safe on idempotent calls.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  type: api_error
                  code: internal_error
                  message: >-
                    An internal error occurred. The request may be retried with
                    the same Idempotency-Key.
                  doc_url: >-
                    https://vexioncards.mintlify.site/api-reference/errors#internal_error
                  request_id: req_5f8b2c4a9e1d3
              examples:
                Http 500 default:
                  summary: Http 500 default
                  value:
                    error:
                      type: api_error
                      code: internal_error
                      message: >-
                        An internal error occurred. The request may be retried
                        with the same Idempotency-Key.
                      doc_url: >-
                        https://vexioncards.mintlify.site/api-reference/errors#internal_error
                      request_id: req_5f8b2c4a9e1d3
          headers: {}
      deprecated: false
components:
  schemas:
    Customer:
      type: object
      required:
        - id
        - object
        - card_id
      properties:
        id:
          type: string
          description: >-
            Customer identifier - an opaque, server-issued token prefixed cus_.
            Encodes the internal record through a keyed cipher (AES-128 + an
            integrity tag, base32-encoded) - not decodable or guessable
            client-side; the prefix lets clients route on resource type without
            parsing the ID.
          pattern: ^cus_[A-Za-z0-9]+$
          example: cus_g2y5z2ylfkrybsthe2qnweualy
        object:
          type: string
          const: customer
          description: >-
            Always 'customer'. Type discriminator for consumers that handle
            multiple resource families.
        external_id:
          description: >-
            Your stable reference for this customer (CRM ID, internal account
            number, etc.). Highest-priority dedupe field on POST.
          type:
            - string
            - 'null'
        email:
          format: email
          description: >-
            Customer's email address. Used for wallet pass delivery, account
            recovery, and dedupe.
          maxLength: 255
          example: customer@example.com
          type:
            - string
            - 'null'
        phone:
          description: >-
            Customer's phone number in E.164 format. Used for SMS pass delivery
            and dedupe.
          pattern: ^\+[1-9]\d{1,14}$
          minLength: 8
          maxLength: 16
          example: '+14155552671'
          type:
            - string
            - 'null'
        first_name:
          description: Given name. Shown on the wallet pass when configured.
          type:
            - string
            - 'null'
        last_name:
          description: Family name. Shown on the wallet pass when configured.
          type:
            - string
            - 'null'
        date_of_birth:
          format: date
          description: >-
            Customer's date of birth in ISO 8601 format (YYYY-MM-DD). Used for
            birthday campaigns and age-gated rewards.
          type:
            - string
            - 'null'
        language:
          type: string
          enum:
            - sk
            - cs
            - en
            - de
            - hu
            - hr
            - ro
          description: >-
            Preferred display language for the wallet pass and notifications.
            Writable values are sk, cs, en, de; reads may additionally return
            hu, hr, or ro for legacy customers enrolled before the language set
            was narrowed.
        marketing_opt_in:
          type: boolean
          description: >-
            A record of the customer's marketing consent, stored and returned
            for your own use and audits. VEXiON does not apply it when sending
            notifications: a notification sent through the API is not filtered
            by this flag or by the customer's marketing switch in the dashboard.
            To respect opt-outs on API sends, send only to customers with
            marketing_opt_in true, using explicit `customer_ids`.
        gdpr_redacted_at:
          description: >-
            Unix timestamp when the customer requested GDPR
            right-to-be-forgotten. When set, all PII fields are anonymized but
            the skeleton record is preserved for analytics integrity. Distinct
            from deleted_at (soft-delete from admin or DELETE
            /v1/customers/{id}).
          example: null
          type:
            - integer
            - 'null'
        card_id:
          type: string
          description: Card template (crd_) this customer is enrolled in. Immutable.
        card_type:
          type: string
          enum:
            - stamp
            - reward
            - discount
            - coupon
            - membership
            - gift_card
            - custom
          description: >-
            Echo of the parent card's type for routing convenience. Mirrors
            card.type.
        card:
          $ref: '#/components/schemas/CustomerCardSlot'
          description: >-
            Type-specific mechanics data (stamps collected, points balance,
            tier, gift-card balance, etc.). Shape varies per card_type.
        signup_location_id:
          description: >-
            Location whose signup QR/link enrolled this customer (`?loc=` on the
            signup URL). Null for customers created via API or dashboard - only
            public signup-form enrollments carry it.
          type:
            - string
            - 'null'
        status:
          enum:
            - installed
            - not_installed
            - deleted
            - disputed
            - null
          description: >-
            Wallet pass install state on the customer's device. installed = pass
            on at least one device, not_installed = signed up but pass not yet
            added, deleted = customer removed the pass, disputed = wallet
            flagged the pass. A customer with no stored status reads
            `not_installed`; the value is never null.
          type:
            - string
            - 'null'
        device:
          enum:
            - apple_wallet
            - google_wallet
            - null
          description: >-
            Wallet platform holding the pass: `apple_wallet` or `google_wallet`.
            Null when no wallet pass is installed. The platform tracks Apple and
            Google Wallet only - any other install channel reads as null.
          type:
            - string
            - 'null'
        installed_at:
          description: >-
            Unix seconds when the pass was FIRST added to a wallet (Apple/Google
            install callback). Null until installed. Distinct from `created_at`
            (when the customer record was created at signup) - the gap between
            the two is your activation funnel.
          example: 1779494400
          type:
            - integer
            - 'null'
        apple_wallet_url:
          description: >-
            Direct download URL for the .pkpass file. Sent to iPhone customers
            via SMS or email.
          type:
            - string
            - 'null'
        google_wallet_url:
          description: >-
            Save-to-Google-Wallet URL for this customer's card, or `null` if not
            yet generated. Generated lazily: `null` on a freshly created
            customer, and populated on the first `GET /v1/customers/{id}` for
            that customer (not on create, and not on the list endpoint) -
            persisted after the first successful generation, so later reads
            return the same URL without regenerating it. A transient generation
            failure leaves this `null`; retry by reading the customer again.
          type:
            - string
            - 'null'
        claim_url:
          type: string
          description: >-
            Public claim URL the customer visits to add the pass to their
            wallet. Auto-detects device and serves the right format. Treat the
            URL as opaque - never construct or parse it; its internal format may
            change.
        serial_number:
          type: string
          description: >-
            The value encoded in the customer's wallet pass QR code or barcode,
            and the number printed on physical cards. A merchant-assigned custom
            number when `serial_number_locked` is true; otherwise a
            platform-assigned 24-character hex value, or a referral link ending
            in it when the card has referrals enabled. NOT the customer `id`: to
            find the customer behind a scanned code, call `GET
            /v1/customers?serial_number=<scanned value>`.
        serial_number_locked:
          type: boolean
          description: >-
            True when the serial number was custom-set by staff (do not
            regenerate). False when auto-generated.
        custom:
          type: object
          additionalProperties: true
          description: >-
            Custom field VALUES for this customer, keyed by the definition's
            `key`. Validated against the card's form and the `cf_` definitions -
            unknown keys are rejected (`422 unknown_custom_field`).


            Value shapes by field type: `text`/`textarea` → string; `number` →
            number (integer when the definition says so); `currency` → integer
            minor units; `date` → `YYYY` / `YYYY-MM` / `YYYY-MM-DD` per
            granularity; `boolean` → true/false; `select` → one option value;
            `multiselect` → array of option values.


            Renderable everywhere via `{{custom.<key>}}`: pass slots,
            notification title/body - typed values format in the customer's
            locale.
          properties: {}
        metadata:
          type: object
          description: >-
            Partner-internal `{key: value}` map (max 50 keys, key
            `^[a-zA-Z0-9_]{1,40}$`, string values ≤500 chars). NEVER rendered to
            the customer - for CRM ids, import batches, integration markers.
            Customer-facing typed data belongs in `custom` (validated against
            custom field definitions, renderable on passes/notifications).


            **Update semantics (JSON Merge Patch):** PATCH merges per key - send
            only the keys you change; `"key": null` deletes that key;
            `"metadata": null` clears the whole map; omitting `metadata` leaves
            it untouched.
          additionalProperties:
            type: string
            maxLength: 500
          maxProperties: 50
          propertyNames:
            pattern: ^[a-zA-Z0-9_]{1,40}$
          properties: {}
        last_delivery:
          properties:
            channel:
              type: string
              enum:
                - email
                - sms
                - both
            status:
              type: string
              enum:
                - sent
                - failed
              description: >-
                `sent` = the provider accepted the message (not proof of
                receipt); `failed` = provider rejection (bad number, bounced
                address).
            to:
              type: string
            sent_at:
              type: integer
            error:
              type:
                - string
                - 'null'
              description: >-
                Provider failure reason when `status: "failed"` (bounced
                address, invalid number). Null on success.
          description: >-
            Most recent email/SMS delivery attempt for this customer's wallet
            pass. Null if never delivered via API. Contains channel, destination
            (redacted email/phone), status, sent_at timestamp, and error message
            if delivery failed.
          type:
            - object
            - 'null'
        referral_stats:
          description: >-
            Referral program statistics for this customer. Null when the card
            template has no referral program configured. All counts are
            non-negative integers.
          properties:
            total_referrals:
              type: integer
              minimum: 0
              description: >-
                Total referrals sent by this customer (whether they converted or
                not).
              example: 3
            successful_referrals:
              type: integer
              minimum: 0
              description: Referrals that converted into active customers.
              example: 2
            pending_referrals:
              type: integer
              minimum: 0
              description: >-
                Referrals sent but not yet converted. A referral waits 90 days
                after the friend signs up for the first purchase; after that it
                expires and is not counted here.
              example: 1
            total_bonuses_earned:
              type: integer
              minimum: 0
              description: >-
                Total bonus stamps or points awarded for successful referrals -
                the same unit as the card's
                `referral.referrer_bonus`/`referral.referee_bonus`, and as
                `unit` on this object names directly (no need to cross-reference
                the enrolled card's `type` separately anymore).
              example: 200
            unit:
              type: string
              enum:
                - stamps
                - points
              description: >-
                What `total_bonuses_earned` counts, resolved from the enrolled
                card's type: `stamp` cards award `stamps`; every other card type
                (`reward`, including cashback redemption mode, which rides the
                same points ledger as any other Reward card) awards `points`.
                Mirrors the `unit` convention on Transaction.
              example: points
            last_referral_at:
              description: >-
                Unix timestamp of most recent referral sent. Null if never
                referred.
              example: 1782000000
              type:
                - integer
                - 'null'
          required:
            - total_referrals
            - successful_referrals
            - pending_referrals
            - total_bonuses_earned
            - unit
          type:
            - object
            - 'null'
        created_at:
          type: integer
          description: Unix timestamp when the customer record was created (signup time).
        updated_at:
          type: integer
          description: Unix timestamp when any field on the customer was last modified.
        last_visited_at:
          description: >-
            Unix seconds of the customer's most recent confirmed transaction -
            ANY channel (POS, scanner, API, online). Stored denormalized for
            fast filtering; equals the newest ledger entry (see GET
            /v1/transactions), except that gift card lock, employee refill and
            merchant issue rows do not move it.
          example: 1782604800
          type:
            - integer
            - 'null'
        level_valid_until:
          description: >-
            Unix seconds until which the customer keeps the current discount or
            reward tier (level retention, `mechanics.tier_demotion`). It is the
            end of the Bratislava calendar day: a qualifying transaction on or
            before that day keeps the level, and the first daily run after it
            moves the customer down one tier. Null when level retention is off
            on the card, the customer is on the lowest tier, the card cannot use
            level retention (employee discount, Booqme or Bookio card, flat
            reward, fewer than 2 tiers), or the card entry is deleted or
            disputed. Reminders go out 14 and 3 days before this date.
          example: 1807833599
          type:
            - integer
            - 'null'
        last_visited_location_id:
          description: >-
            Location of the most recent confirmed transaction - ANY channel.
            Stored denormalized for fast filtering; equals the newest ledger
            entry (see GET /v1/transactions), except that gift card lock,
            employee refill and merchant issue rows do not move it.
          example: loc_g2f2cbt7cbuz3hambazorvws2q
          type:
            - string
            - 'null'
        deleted_at:
          description: >-
            Unix seconds when the customer was soft-deleted (DELETE
            /v1/customers/{id} or dashboard). Null while active. Soft-deleted
            customers keep their FULL transaction ledger - history is never
            destroyed. Distinct from `gdpr_redacted_at` (right-to-be-forgotten
            anonymization).
          type:
            - integer
            - 'null'
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - type
            - code
            - message
          properties:
            type:
              type: string
              enum:
                - invalid_request_error
                - authentication_error
                - permission_error
                - not_found_error
                - rate_limit_error
                - idempotency_error
                - api_error
            code:
              type: string
            message:
              type: string
            retry_after:
              type: integer
              description: >-
                Seconds to wait before retrying. Present on rate-limit (429) and
                idempotency-key-in-use (409) errors; mirrors the `Retry-After`
                HTTP header. Absent on error types that carry no retry guidance.
              example: 5
            param:
              type:
                - string
                - 'null'
            request_id:
              type: string
            doc_url:
              type: string
              format: uri
            field_errors:
              type: array
              description: >-
                Per-field errors on a 422. At most 100 entries: a request with
                more errors gets the first 100.
              items:
                type: object
                properties:
                  param:
                    type: string
                  code:
                    type: string
                  message:
                    type: string
                  expected_currency:
                    type: string
                    description: >-
                      On `currency_mismatch`: the business currency the request
                      must use. Absent on every other field error.
          description: >-
            The error payload: type (category), code (machine-readable), message
            (human-readable), retry_after (seconds, on
            rate-limit/idempotency-in-use errors only), param (offending field),
            request_id, doc_url, field_errors[].
      description: >-
        Standard error envelope. Branch on `error.code`; include
        `error.request_id` in support requests. See the Errors guide.
    CustomerCardSlot:
      type: object
      description: >-
        Per-customer single card slot, trimmed by the parent card_type. Common
        fields (visit + transaction aggregates, slot timestamps) appear on every
        type; the type-specific block is selected by card_type. Points and
        cashback balance never coexist on one reward slot. Lifetime aggregates
        live in /v1/transactions, not here. See the Customers API reference for
        the per-type field tables.
      required:
        - card_id
        - card_type
      additionalProperties: false
      properties:
        card_id:
          type: string
          description: Public id of the parent card template (crd_).
        card_type:
          type: string
          enum:
            - stamp
            - reward
            - discount
            - coupon
            - membership
            - gift_card
            - custom
          description: >-
            Branch on this to read the variant fields. 'custom' carries no
            type-specific block - only the common fields above.
        status:
          type:
            - string
            - 'null'
          enum:
            - installed
            - not_installed
            - deleted
            - disputed
            - null
          description: Pass-install status for this slot.
        total_visits:
          type: integer
          description: Lifetime visit count on this slot.
        first_transaction_at:
          type:
            - integer
            - 'null'
          description: Unix seconds of the first transaction on this slot.
        last_transaction_at:
          type:
            - integer
            - 'null'
          description: Unix seconds of the most recent transaction on this slot.
        created_at:
          type: integer
          description: Unix seconds the slot was created.
        updated_at:
          type: integer
          description: Unix seconds the slot was last updated.
        remaining_stamps:
          type: integer
          description: 'Stamp only: stamps left until the next reward.'
        total_stamps:
          type: integer
          description: 'Stamp only: stamps required to complete one row.'
        stamps_pending:
          type: integer
          description: 'Stamp only: stamps awaiting confirmation.'
        points:
          type: number
          description: >-
            Reward (points modes) only: current points balance. Can carry up to
            4 decimals (see Transaction `value`).
        points_pending:
          type: number
          description: 'Reward (points modes) only: points awaiting confirmation.'
        reward_tier:
          type:
            - object
            - 'null'
          description: >-
            Reward cards with levels in use (tiered earn mode and at least one
            tier) only, points and cashback alike: the customer's current tier.
            The name and the multiplier are resolved live from the stored spend
            against the card's current tiers, so a tier edit shows at once.
            Absent when the card's levels are off. `tier_effective_date` and
            `previous_tier_name` are as stored and can lag `tier_name` after a
            tier edit.
          additionalProperties: false
          properties:
            tier_name:
              type:
                - string
                - 'null'
            earn_multiplier:
              type: number
            tier_effective_date:
              type:
                - integer
                - 'null'
            previous_tier_name:
              type:
                - string
                - 'null'
        cashback_balance_minor:
          type: integer
          description: >-
            Reward (cashback redemption mode) only: cashback balance in minor
            units. This is the credit the customer sees on the wallet pass and
            in push messages (points x `cashback_point_value`). On a card
            connected to a till that sets its own point value (Dotykacka,
            Storyous, Papaya, Oberon), the rate of the till is used, as on the
            wallet pass.
        cashback_pending_minor:
          type:
            - integer
            - 'null'
          description: >-
            Reward (cashback redemption mode) only. Always `null` in the current
            implementation - cashback redemptions finalize immediately at the
            till, so there is no pending/reservation state to report. Reserved
            for a possible future async confirmation flow.
        discount:
          type: object
          description: 'Discount only: current discount state.'
          additionalProperties: false
          properties:
            discount_level:
              type:
                - integer
                - 'null'
              description: Current tier percent off.
            period_spend_minor:
              type:
                - integer
                - 'null'
              description: Spend accumulated in the current window, in minor units.
            period_start_date:
              type:
                - string
                - 'null'
              description: YYYY-MM-DD the current window began.
            next_reset_date:
              type:
                - string
                - 'null'
              description: YYYY-MM-DD the window resets.
        coupon_status:
          type:
            - string
            - 'null'
          description: >-
            Coupon only: `active`, `used`, `redeemed` or `expired`. Always
            lowercase on the wire. The platform stores this value mixed case
            internally for historical reasons; the API normalises it, so branch
            on the lowercase form. `used` and `expired` are reserved: no current
            code path produces them, so a branch on either is dead code today.
          enum:
            - active
            - used
            - redeemed
            - expired
            - null
        membership:
          type: object
          description: 'Membership only: current membership state.'
          additionalProperties: false
          properties:
            is_active:
              type: boolean
            activated_at:
              type:
                - integer
                - 'null'
            expires_at:
              type:
                - integer
                - 'null'
            last_used_at:
              type:
                - integer
                - 'null'
            uses_today:
              type: integer
            total_uses:
              type: integer
        balance_minor:
          type: integer
          description: 'Gift card only: current balance in minor units.'
        expires_at:
          type:
            - integer
            - 'null'
          description: 'Gift card only: unix seconds the balance expires.'
        currency:
          $ref: '#/components/schemas/Currency'
          description: >-
            The business currency of the card. It is the currency of
            `cashback_balance_minor` and `balance_minor`. Also sent on the
            customer webhooks.
    Currency:
      type: string
      enum:
        - EUR
        - CZK
        - GBP
        - HUF
        - PLN
        - RON
        - CHF
        - USD
      description: >-
        Three-letter ISO 4217 code. The 8 currencies a business can use: EUR,
        CZK, GBP, HUF, PLN, RON, CHF, USD. Amounts in this API are minor units
        with 2 decimals for every currency, also HUF. See [Money and
        currency](/api-reference/money-and-currency).
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: >-
        Your secret API key as a Bearer token: `Authorization: Bearer
        vxn_sk_...`. Keys are created and scoped in the dashboard - see
        [Authentication](/api-reference/introduction#authentication).

````

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