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

# Clientes (CRM)

> Perfil de compra pseudonimizado dos clientes de uma loja — RFV, ticket médio e frequência, sem dados pessoais

Retorna a base de clientes de uma loja sob a ótica do CRM: para cada cliente, o **perfil de compra** calculado pela Repediu — quantas vezes comprou, quanto gastou, ticket médio, intervalo entre compras, melhor dia da semana e status RFV.

<Info>
  Os dados são **pseudonimizados**: não há nome, telefone nem e-mail. O `customerId` é o elo para cruzar com [Conversões](/crm-parceiro/conversoes), [Cupons](/crm-parceiro/cupons) e [Avaliações](/crm-parceiro/avaliacoes) sem expor dado pessoal. Se precisar do cadastro do cliente (contatos, endereços), use [Obter cliente (CRM)](/crm-parceiro/obter-cliente).
</Info>

<Note>
  A paginação é por **cursor opaco**: repita a chamada passando `cursor` com o valor de `nextCursor` da página anterior, até `nextCursor` vir `null`. `limit` padrão 100, máximo 500 (valores maiores são reduzidos para 500 silenciosamente). Para sincronização incremental, use `updatedSince` — só retorna clientes atualizados a partir daquele instante.
</Note>

Os campos de opt-in (`whatsappOptin`, `emailOptin`) dizem se o cliente aceita comunicação em cada canal — úteis para estimar o alcance de uma campanha antes de criá-la. `rfvStatus` é o nome do status em português (por exemplo `Campeões`, `Fiéis`, `Em risco`, `Promissores`, `Perdidos`) e `bestWeekday` é o índice numérico do dia da semana em que o cliente mais compra (0 = domingo).

## Erros

| HTTP  | `code` no corpo | Quando acontece                                   | O que fazer                                                            |
| ----- | --------------- | ------------------------------------------------- | ---------------------------------------------------------------------- |
| `400` | `BAD_REQUEST`   | `updatedSince` mal formatado ou `cursor` inválido | Corrija o parâmetro indicado em `details`                              |
| `401` | `UNAUTHORIZED`  | Token ausente, inválido ou expirado               | Gere um novo token em [Obter token](/open-delivery/obter-token)        |
| `403` | `FORBIDDEN`     | Token sem o escopo exigido pela rota              | Confirme as permissões da credencial com a Repediu                     |
| `404` | `NOT_FOUND`     | Merchant inexistente **ou fora do escopo**        | Confirme o `id` em [Listar merchants](/open-delivery/listar-merchants) |

<Card title="Obter cliente (CRM)" icon="user" href="/crm-parceiro/obter-cliente">
  A ficha completa de um único cliente pelo `customerId` — incluindo contato e histórico.
</Card>


## OpenAPI

````yaml openapi.yaml GET /v1/stores/{merchantId}/customers
openapi: 3.0.1
info:
  title: Repediu — API de integração
  description: >-
    API de integração da Repediu: rotas Open Delivery 2.0 (capabilities CRM),
    dado bruto do CRM do parceiro (/v1/stores) e métricas agregadas
    (/v1/stores/.../metrics). A autenticação usa o fluxo client_credentials: o
    token identifica a aplicação integradora e dá acesso a todos os
    estabelecimentos (merchants) que ativaram a integração.
  version: v2
servers:
  - url: https://public-api.repediu.com.br
    description: Produção
security:
  - Bearer: []
paths:
  /v1/stores/{merchantId}/customers:
    get:
      tags:
        - CRM do parceiro
      summary: Clientes (CRM)
      description: >-
        Perfil de compra pseudonimizado dos clientes da loja — RFV, ticket
        médio, frequência e opt-ins, sem nome, telefone ou e-mail. Paginação por
        cursor opaco.
      operationId: listStoreCustomers
      parameters:
        - $ref: '#/components/parameters/MerchantIdPath'
        - name: updatedSince
          in: query
          description: >-
            Retorna apenas clientes atualizados a partir deste instante
            (sincronização incremental).
          schema:
            type: string
            format: date-time
        - $ref: '#/components/parameters/CrmCursor'
        - $ref: '#/components/parameters/CrmLimit'
      responses:
        '200':
          description: Página de clientes da loja, ordenada por atualização.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CrmCustomerListResponse'
              example:
                items:
                  - customerId: 18452
                    firstPurchaseAt: '2025-11-02T09:15:00'
                    lastPurchaseAt: '2026-08-14T20:30:00'
                    purchaseCount: 14
                    totalSpent: 1063.6
                    avgTicket: 75.97
                    avgDaysBetweenPurchases: 21
                    bestWeekday: 5
                    rfvStatus: Fiéis
                    whatsappOptin: true
                    emailOptin: false
                    createdAt: '2025-11-02T12:15:00+00:00'
                    updatedAt: '2026-08-14T23:41:33+00:00'
                nextCursor: null
        '400':
          description: updatedSince mal formatado ou cursor inválido.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: BAD_REQUEST
                message: One or more validation errors occurred
                details:
                  - cursor is invalid
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/InsufficientScope'
        '404':
          $ref: '#/components/responses/MerchantNotFound'
components:
  parameters:
    MerchantIdPath:
      name: merchantId
      in: path
      required: true
      description: Identificador (UUID) do merchant — o mesmo id de GET /od/v2/merchants.
      schema:
        type: string
        format: uuid
    CrmCursor:
      name: cursor
      in: query
      description: >-
        Cursor opaco da próxima página, retornado em nextCursor. Omita na
        primeira chamada.
      schema:
        type: string
    CrmLimit:
      name: limit
      in: query
      description: Itens por página. Valores acima de 500 são limitados a 500.
      schema:
        type: integer
        format: int32
        default: 100
        maximum: 500
  schemas:
    CrmCustomerListResponse:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/CrmCustomerItem'
        nextCursor:
          type: string
          nullable: true
          description: Cursor da próxima página. Null quando não há próxima página.
    ErrorResponse:
      type: object
      properties:
        code:
          type: string
          description: >-
            Código do erro (BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, NOT_FOUND,
            INTERNAL_SERVER_ERROR).
        message:
          type: string
        details:
          type: array
          nullable: true
          description: Lista de mensagens de validação, quando aplicável.
          items:
            type: string
    CrmCustomerItem:
      type: object
      properties:
        customerId:
          type: integer
          format: int32
          description: >-
            Identificador do cliente na loja — cruza com conversions, coupons e
            ratings.
        firstPurchaseAt:
          type: string
          format: date-time
          nullable: true
        lastPurchaseAt:
          type: string
          format: date-time
          nullable: true
        purchaseCount:
          type: integer
          format: int32
        totalSpent:
          type: number
          description: Total gasto pelo cliente, em reais.
        avgTicket:
          type: number
          description: Ticket médio do cliente, em reais.
        avgDaysBetweenPurchases:
          type: integer
          format: int32
        bestWeekday:
          type: integer
          format: int32
          description: Dia da semana em que o cliente mais compra (0 = domingo).
        rfvStatus:
          type: string
          description: >-
            Nome do status RFV em português (Campeões, Fiéis, Em risco,
            Promissores, Perdidos).
        whatsappOptin:
          type: boolean
        emailOptin:
          type: boolean
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
  responses:
    Unauthorized:
      description: Token ausente, inválido ou expirado.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: UNAUTHORIZED
            message: Invalid client credentials.
            details: null
    InsufficientScope:
      description: Token sem o escopo exigido pela rota.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: FORBIDDEN
            message: The access token does not have the required scope.
            details: null
    MerchantNotFound:
      description: Merchant inexistente ou fora do escopo da aplicação.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: NOT_FOUND
            message: Merchant not found.
            details: null
  securitySchemes:
    Bearer:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        Token de acesso emitido por POST /od/v2/oauth/token (fluxo
        client_credentials). Envie no header Authorization como `Bearer
        <token>`.

````