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

# List customers

> The buyers with a counted order in the installation's stores. `name` and `phone` only with `read_customer_contact`.

Needs the `read_customers` scope (permission `customers:read`).



## OpenAPI

````yaml /openapi/openapi.2026-10.json get /apps/2026-10/customers
openapi: 3.1.0
info:
  title: Tassyir Admin API
  version: 2026-10
  summary: >-
    The API installed apps call to read and change a Tassyir business, and the
    webhooks they receive.
  description: >-
    Version `2026-10` of the API installed Tassyir apps call. The routes live
    under `/apps/2026-10/`; the token endpoint and the key documents under
    `/apps/`. Guides, and a page for every error code:
    https://developers.tassyir.io.


    **Authentication.** `Authorization: Bearer tsy_at_…` from `POST
    /apps/oauth/token`. An app acts as a team member of the business: it reads
    and writes only what its granted scopes allow, only in the stores the
    merchant gave it, under the merchant's plan.


    **Conventions.** JSON fields are camelCase; ids are strings; times are ISO
    8601 UTC; money is a decimal string (`"2500.00"`) with a `currency` field,
    never a number. A list answers `{ data, nextCursor }`, newest first,
    keyset-paged: `limit` 1 to 100 (default 50), `cursor` = the previous
    `nextCursor`. A single resource answers `{ data }`. `fields` trims a
    resource to the named top-level fields. Fields without
    `read_customer_contact` (buyer name, phones, address, notes) are omitted,
    not nulled. Every answer is `Cache-Control: no-store`.


    **Writes.** Send an `Idempotency-Key` to make a write safe to retry (24
    hours per installation).


    **Errors.** `application/problem+json` with a stable `code`, a `fix`
    sentence, the `docs` page of the code and a `requestId`. Each operation
    lists every code it can answer; `x-tassyir-errors` lists every code a
    problem can carry, with its title, statuses and `fix`. HTTP 460
    `STORE_SCOPE_FORBIDDEN` means a store outside the installation's stores.


    **Rate limits.** A leaky bucket per installation, by default 40 requests
    refilled at 4 a second: every answer carries `RateLimit-Limit`,
    `RateLimit-Remaining` and `RateLimit-Reset`; a refusal is 429 `RATE_LIMITED`
    with `Retry-After`. All the apps of one business also share a ceiling, by
    default 20 requests a second: 429 `BUSINESS_RATE_LIMITED` with
    `Retry-After`. An API process that is already running its maximum of app
    requests answers 503 `API_BUSY` with `Retry-After`, having done nothing.


    **Webhooks.** Signed Standard Webhooks deliveries (`v1a`, Ed25519), one per
    topic below; verify them with `GET /apps/webhook-keys`.
servers:
  - url: https://api.tassyir.io
security:
  - appAccessToken: []
tags:
  - name: Authentication
    description: Tokens and the public keys apps verify with.
  - name: Business
    description: The business the app is installed on.
  - name: Orders
    description: Orders, their comments and the cancel reasons.
  - name: Products
    description: Products and their variants.
  - name: Collections
    description: Collections and their members.
  - name: Inventory
    description: Stock levels.
  - name: Customers
    description: The buyers behind the orders.
  - name: Stores
    description: The installation's stores.
  - name: Delivery
    description: Delivery agents and fee groups (never credentials).
  - name: Data
    description: >-
      Typed values the app keeps on the business, its stores, products, orders
      and customers, under keys its manifest declares.
  - name: Images
    description: >-
      Images re-hosted on the Tassyir CDN, for the product and collection
      writes.
  - name: Bulk exports
    description: >-
      A whole list (orders, products, customers) as one JSON Lines file, instead
      of paging through it.
  - name: Webhooks
    description: What Tassyir POSTs to the app.
paths:
  /apps/2026-10/customers:
    get:
      tags:
        - Customers
      summary: List customers
      description: >-
        The buyers with a counted order in the installation's stores. `name` and
        `phone` only with `read_customer_contact`.


        Needs the `read_customers` scope (permission `customers:read`).
      operationId: listCustomers
      parameters:
        - name: storeId
          in: query
          required: false
          schema:
            type: string
            pattern: ^\d{1,24}$
          description: >-
            Only this store's rows. A store outside the installation's stores is
            460 `STORE_SCOPE_FORBIDDEN`.
        - name: fields
          in: query
          required: false
          schema:
            maxLength: 1000
            type: string
          description: >-
            Return only these top-level fields, separated by commas; the id is
            always kept. The response schema describes the whole resource. An
            unknown name is 400 `INVALID_FIELDS`. Fields: `id`, `provinceCode`,
            `townOns`, `name`, `phone`, `ordersCount`, `openCount`,
            `deliveredCount`, `returnedCount`, `canceledCount`, `currency`,
            `deliveredValue`, `firstOrderAt`, `lastOrderAt`, `blocked`,
            `createdAt`, `updatedAt`.
        - name: limit
          in: query
          required: false
          schema:
            maximum: 100
            minimum: 1
            type: integer
          description: >-
            Page size, 1 to 100 (default 50). A value outside that range is 400,
            never clamped.
        - name: cursor
          in: query
          required: false
          schema:
            maxLength: 512
            type: string
          description: >-
            The `nextCursor` of the previous page, unchanged, with the same
            filters. Opaque; bound to this list and its filters (400
            `INVALID_CURSOR` otherwise).
      responses:
        '200':
          description: A page of customers.
          headers:
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimit-Limit'
            RateLimit-Remaining:
              $ref: '#/components/headers/RateLimit-Remaining'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimit-Reset'
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
            Cache-Control:
              $ref: '#/components/headers/Cache-Control-No-Store'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerPage'
        '400':
          description: >-
            Bad request: `INVALID_CURSOR`, `INVALID_FIELDS`,
            `VALIDATION_FAILED`.
          headers:
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimit-Limit'
            RateLimit-Remaining:
              $ref: '#/components/headers/RateLimit-Remaining'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimit-Reset'
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/problem+json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Problem'
                  - properties:
                      status:
                        const: 400
                      code:
                        enum:
                          - INVALID_CURSOR
                          - INVALID_FIELDS
                          - VALIDATION_FAILED
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: 'Not allowed: `PERMISSION_DENIED`.'
          headers:
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimit-Limit'
            RateLimit-Remaining:
              $ref: '#/components/headers/RateLimit-Remaining'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimit-Reset'
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/problem+json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Problem'
                  - properties:
                      status:
                        const: 403
                      code:
                        enum:
                          - PERMISSION_DENIED
        '429':
          $ref: '#/components/responses/RateLimited'
        '460':
          description: 'Store outside the installation: `STORE_SCOPE_FORBIDDEN`.'
          headers:
            RateLimit-Limit:
              $ref: '#/components/headers/RateLimit-Limit'
            RateLimit-Remaining:
              $ref: '#/components/headers/RateLimit-Remaining'
            RateLimit-Reset:
              $ref: '#/components/headers/RateLimit-Reset'
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/problem+json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Problem'
                  - properties:
                      status:
                        const: 460
                      code:
                        enum:
                          - STORE_SCOPE_FORBIDDEN
        '500':
          $ref: '#/components/responses/InternalError'
        '503':
          $ref: '#/components/responses/Unavailable'
components:
  headers:
    RateLimit-Limit:
      description: >-
        The installation's request bucket (default 40); on a 429
        `BUSINESS_RATE_LIMITED`, the business's ceiling (default 20 a second).
        Absent when a request is refused before the rate limits run (an
        unparsable body, a 503 `API_BUSY`).
      schema:
        minimum: 1
        type: integer
    RateLimit-Remaining:
      description: Requests still accepted right now.
      schema:
        type: integer
        minimum: 0
    RateLimit-Reset:
      description: >-
        Seconds until the bucket is empty again (it drains at 4 requests a
        second by default).
      schema:
        type: integer
        minimum: 0
    X-Request-Id:
      description: The request's id, also in an error's `requestId`.
      schema:
        type: string
    Cache-Control-No-Store:
      description: '`no-store`: never cache an answer.'
      schema:
        type: string
        const: no-store
    Retry-After:
      description: Seconds to wait before retrying.
      schema:
        type: integer
        minimum: 0
  schemas:
    CustomerPage:
      type: object
      description: >-
        One page, newest first. Pass `nextCursor` back as `cursor` for the next
        one.
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Customer'
        nextCursor:
          anyOf:
            - type: string
            - type: 'null'
          description: Null on the last page.
      required:
        - data
        - nextCursor
      additionalProperties: false
    Problem:
      type: object
      description: >-
        An Admin API error (RFC 9457 problem details), served as
        `application/problem+json`. Act on `code`, never on `detail`. Other
        members name the subject of the refusal (`permission`, `storeId`,
        `feature`, `limit`, `errors`, …).
      properties:
        type:
          type: string
          format: uri
          description: >-
            `https://developers.tassyir.io/errors/<code in kebab case>`:
            identifies the code, and is the code's page.
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
          description: >-
            For a person. A 5xx never carries an internal message: `requestId`
            names it.
        code:
          type: string
          pattern: ^[A-Z][A-Z0-9_]{1,63}$
          description: 'Stable: what the app acts on.'
        fix:
          type: string
          description: One sentence an AI agent can act on.
        docs:
          type: string
          format: uri
          description: >-
            The page that explains the code and what to do: the same URL as
            `type`.
        requestId:
          type: string
          description: Also in the `X-Request-Id` header. Quote it to Tassyir.
        errors:
          type: array
          items:
            type: string
          description: 'With `VALIDATION_FAILED`: one entry per invalid parameter.'
      required:
        - type
        - title
        - status
        - detail
        - code
        - docs
    Customer:
      type: object
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 64
          description: A resource id (a snowflake id, as a string).
        provinceCode:
          anyOf:
            - type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            - type: 'null'
        townOns:
          anyOf:
            - type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            - type: 'null'
        name:
          anyOf:
            - type: string
            - type: 'null'
          description: read_customer_contact only.
        phone:
          type: string
          description: read_customer_contact only.
        ordersCount:
          type: integer
          minimum: 0
          maximum: 9007199254740991
          description: >-
            Orders placed (exchanges, waiting, archived and abandoned ones
            excluded).
        openCount:
          type: integer
          minimum: 0
          maximum: 9007199254740991
        deliveredCount:
          type: integer
          minimum: 0
          maximum: 9007199254740991
        returnedCount:
          type: integer
          minimum: 0
          maximum: 9007199254740991
        canceledCount:
          type: integer
          minimum: 0
          maximum: 9007199254740991
        currency:
          type: string
          pattern: ^[A-Z]{3}$
          description: ISO 4217 currency code of every amount in this resource.
        deliveredValue:
          type: string
          pattern: ^-?\d+\.\d{2}$
          description: 'Spent: delivered orders only.'
        firstOrderAt:
          anyOf:
            - type: string
              format: date-time
              pattern: >-
                ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
              description: ISO 8601 date-time, UTC.
            - type: 'null'
        lastOrderAt:
          anyOf:
            - type: string
              format: date-time
              pattern: >-
                ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
              description: ISO 8601 date-time, UTC.
            - type: 'null'
        blocked:
          type: boolean
          description: The merchant blocked this buyer.
        createdAt:
          type: string
          format: date-time
          pattern: >-
            ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
          description: ISO 8601 date-time, UTC.
        updatedAt:
          type: string
          format: date-time
          pattern: >-
            ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
          description: ISO 8601 date-time, UTC.
      required:
        - id
        - provinceCode
        - townOns
        - ordersCount
        - openCount
        - deliveredCount
        - returnedCount
        - canceledCount
        - currency
        - deliveredValue
        - firstOrderAt
        - lastOrderAt
        - blocked
        - createdAt
        - updatedAt
      additionalProperties: false
      description: A customer (API 2026-10).
  responses:
    Unauthorized:
      description: 'No usable access token: `APP_TOKEN_INVALID`, `APP_TOKEN_REQUIRED`.'
      headers:
        RateLimit-Limit:
          $ref: '#/components/headers/RateLimit-Limit'
        RateLimit-Remaining:
          $ref: '#/components/headers/RateLimit-Remaining'
        RateLimit-Reset:
          $ref: '#/components/headers/RateLimit-Reset'
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
        WWW-Authenticate:
          description: The Bearer challenge (RFC 6750).
          schema:
            type: string
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 401
                  code:
                    enum:
                      - APP_TOKEN_INVALID
                      - APP_TOKEN_REQUIRED
    RateLimited:
      description: 'Rate limited: `BUSINESS_RATE_LIMITED`, `RATE_LIMITED`.'
      headers:
        RateLimit-Limit:
          $ref: '#/components/headers/RateLimit-Limit'
        RateLimit-Remaining:
          $ref: '#/components/headers/RateLimit-Remaining'
        RateLimit-Reset:
          $ref: '#/components/headers/RateLimit-Reset'
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
        Retry-After:
          $ref: '#/components/headers/Retry-After'
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 429
                  code:
                    enum:
                      - BUSINESS_RATE_LIMITED
                      - RATE_LIMITED
    InternalError:
      description: 'Internal error: `INTERNAL_ERROR`.'
      headers:
        RateLimit-Limit:
          $ref: '#/components/headers/RateLimit-Limit'
        RateLimit-Remaining:
          $ref: '#/components/headers/RateLimit-Remaining'
        RateLimit-Reset:
          $ref: '#/components/headers/RateLimit-Reset'
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 500
                  code:
                    enum:
                      - INTERNAL_ERROR
    Unavailable:
      description: 'Unavailable: `API_BUSY`, `SERVICE_UNAVAILABLE`.'
      headers:
        RateLimit-Limit:
          $ref: '#/components/headers/RateLimit-Limit'
        RateLimit-Remaining:
          $ref: '#/components/headers/RateLimit-Remaining'
        RateLimit-Reset:
          $ref: '#/components/headers/RateLimit-Reset'
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
        Retry-After:
          $ref: '#/components/headers/Retry-After'
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 503
                  code:
                    enum:
                      - API_BUSY
                      - SERVICE_UNAVAILABLE
  securitySchemes:
    appAccessToken:
      type: http
      scheme: bearer
      bearerFormat: tsy_at_<43 base62>
      description: >-
        An app access token from `POST /apps/oauth/token`, valid 1 hour. An
        offline token acts as the app; an online token acts as the app on behalf
        of one dashboard user (the intersection of both permission sets).

````

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