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

# Sumar puntos a un cliente por una compra

> Acredita puntos de forma inmediata a partir del monto de una compra. Es el endpoint principal para integrar un POS o ERP.

El cliente se identifica por `email` o `dni`. Si no existe, se crea automáticamente, salvo que se mande `ignoreCreateClient: true`.

**Idempotencia**: mandá siempre `external_id` con el identificador de la venta en tu sistema (número de factura, id de ticket). Si llega un `external_id` que ya tiene una operación activa, la API responde 409 en vez de duplicar los puntos. Ese mismo `external_id` es el que se usa para cancelar la operación después.



## OpenAPI

````yaml /api-reference/openapi.json post /external/tags/add
openapi: 3.0.0
info:
  title: API de Tienda de Puntos
  description: >-
    API pública de Tienda de Puntos. Permite sumar puntos por compra, consultar
    clientes, validar canjes y consumir cupones desde tu POS, ERP o e-commerce.


    Todas las llamadas se autentican con el header `x-api-key`.
  version: 0.0.1
  contact: {}
servers:
  - url: https://api.tiendadepuntos.com
    description: Producción
  - url: https://apidev.tiendadepuntos.com
    description: Desarrollo
security:
  - ApiKeyAuth: []
tags: []
paths:
  /external/tags/add:
    post:
      tags:
        - Puntos
      summary: Sumar puntos a un cliente por una compra
      description: >-
        Acredita puntos de forma inmediata a partir del monto de una compra. Es
        el endpoint principal para integrar un POS o ERP.


        El cliente se identifica por `email` o `dni`. Si no existe, se crea
        automáticamente, salvo que se mande `ignoreCreateClient: true`.


        **Idempotencia**: mandá siempre `external_id` con el identificador de la
        venta en tu sistema (número de factura, id de ticket). Si llega un
        `external_id` que ya tiene una operación activa, la API responde 409 en
        vez de duplicar los puntos. Ese mismo `external_id` es el que se usa
        para cancelar la operación después.
      operationId: ExternalTagsController_createPointOperation
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExternalDirectSumRegisterClientDto'
      responses:
        '201':
          description: Puntos acreditados.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  status:
                    type: integer
                    example: 201
                  message:
                    type: string
                    example: Request was successful
                  data:
                    type: object
                    properties:
                      pointOperationId:
                        type: integer
                        example: 998877
                      clientId:
                        type: integer
                        example: 84213
                      points:
                        type: object
                        properties:
                          amount:
                            type: number
                            example: 50
                          totalPointsToGive:
                            type: integer
                            example: 55
                          initialPointsToGive:
                            type: integer
                            example: 50
                required:
                  - success
                  - status
                  - message
        '400':
          description: El cuerpo o los parámetros no pasaron la validación.
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: integer
                    example: 400
                  timestamp:
                    type: string
                    format: date-time
                    example: '2026-07-31T14:05:12.431Z'
                  path:
                    type: string
                    example: /external/tags/add
                  method:
                    type: string
                    example: POST
                  message:
                    type: string
                    example: Error de validación
                  errors:
                    type: array
                    nullable: true
                    items:
                      type: object
                  data:
                    type: object
                    nullable: true
        '403':
          description: El `branch_id` enviado no pertenece al comercio de la API key.
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: integer
                    example: 403
                  timestamp:
                    type: string
                    format: date-time
                    example: '2026-07-31T14:05:12.431Z'
                  path:
                    type: string
                    example: /external/tags/add
                  method:
                    type: string
                    example: POST
                  message:
                    type: string
                    example: Branch with id 1972 does not belong to business with id 42
                  errors:
                    type: array
                    nullable: true
                    items:
                      type: object
                  data:
                    type: object
                    nullable: true
        '404':
          description: >-
            Falta el header `x-api-key` (`Api Key is required`) o la API key no
            corresponde a ningún comercio (`Business not found`). También se usa
            cuando el recurso pedido no existe.
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: integer
                    example: 404
                  timestamp:
                    type: string
                    format: date-time
                    example: '2026-07-31T14:05:12.431Z'
                  path:
                    type: string
                    example: /external/tags/add
                  method:
                    type: string
                    example: POST
                  message:
                    type: string
                    example: Api Key is required
                  errors:
                    type: array
                    nullable: true
                    items:
                      type: object
                  data:
                    type: object
                    nullable: true
        '409':
          description: >-
            Ya existe una operación activa con ese `external_id` para este
            comercio.
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: integer
                    example: 409
                  timestamp:
                    type: string
                    format: date-time
                    example: '2026-07-31T14:05:12.431Z'
                  path:
                    type: string
                    example: /external/tags/add
                  method:
                    type: string
                    example: POST
                  message:
                    type: string
                    example: >-
                      Ya existe una operación de puntos con el externalId
                      FAC-A-0001-00012345 para este negocio
                  errors:
                    type: array
                    nullable: true
                    items:
                      type: object
                  data:
                    type: object
                    nullable: true
      security:
        - ApiKeyAuth: []
components:
  schemas:
    ExternalDirectSumRegisterClientDto:
      type: object
      properties:
        amount:
          type: number
          description: >-
            Monto de la compra en pesos. La conversión a puntos la define la
            regla de puntos configurada por el comercio.
          example: 50
        client:
          description: Datos del cliente que hizo la compra.
          allOf:
            - $ref: '#/components/schemas/ExternalClientDto'
        operator:
          description: Datos del operador o cajero que registró la venta.
          allOf:
            - $ref: '#/components/schemas/ExternalOperatorDto'
        branch_id:
          type: number
          description: >-
            ID de la sucursal donde se hizo la compra. Se obtiene de `GET
            /external/branch`. Debe pertenecer al comercio de la API key.
          example: 1972
        external_id:
          type: string
          description: >-
            Identificador de la transacción en tu sistema (número de factura, id
            de ticket). Es la clave de idempotencia: si repetís un `external_id`
            que ya tiene una operación activa, la API responde 409 en vez de
            duplicar los puntos. También es el identificador con el que después
            se cancela la operación.
          example: FAC-A-0001-00012345
        reason:
          type: string
          description: Observaciones de la suma de puntos.
          example: Suma por compra de 2 medialunas y un cafe latte
        applyBonus:
          description: >-
            Bonificaciones a aplicar sobre los puntos base. Si se omite, no se
            aplica ninguna.
          allOf:
            - $ref: '#/components/schemas/ApplyBonusDto'
        giftCards:
          description: Gift cards de regalo para otro cliente.
          type: array
          items:
            $ref: '#/components/schemas/CreateGiftCardOfferDto'
        ignoreCreateClient:
          type: boolean
          description: >-
            Si llega en `true` y el cliente no existe, la operación falla en vez
            de darlo de alta automáticamente.
          example: false
        origin:
          type: string
          description: >-
            Sistema desde el que se originó la operación. Se usa para
            trazabilidad en los reportes. Si integrás un sistema propio, usá
            `external-integration`.
          example: external-integration
          enum:
            - external-integration
            - dragonfish
            - fudo
            - centum
            - contabilium
            - tiendanube
            - woocommerce
            - shopify
            - desktop-app
            - dux
      required:
        - amount
        - client
    ExternalClientDto:
      type: object
      properties:
        email:
          type: string
          description: >-
            Email del cliente. Obligatorio si no se envía `dni`: hace falta al
            menos uno de los dos para identificar o dar de alta al cliente.
          example: juanma@mailinator.com
        dni:
          type: string
          description: Número de documento del cliente. Obligatorio si no se envía `email`.
          example: '40100800'
        first_name:
          type: string
          description: Nombre del cliente. Se usa al darlo de alta automáticamente.
          example: Juan Manuel
        last_name:
          type: string
          description: Apellido del cliente. Se usa al darlo de alta automáticamente.
          example: Pérez
        phone:
          type: string
          description: Número de teléfono del cliente, con código de país.
          example: '+541164593678'
        birthday:
          type: string
          description: Fecha de nacimiento del cliente
          example: '1990-01-01T00:00:00.000Z'
        gender:
          type: number
          description: >-
            Género del cliente. 0 = masculino, 1 = femenino, 2 = otro, 3 = sin
            datos.
          enum:
            - 0
            - 1
            - 2
            - 3
          example: 0
      required:
        - email
        - dni
    ExternalOperatorDto:
      type: object
      properties:
        email:
          type: string
          description: >-
            Email del operador que registró la venta. Obligatorio si no se envía
            `dni`.
          example: cajero.centro@micomercio.com
        dni:
          type: string
          description: >-
            Número de documento del operador. Obligatorio si no se envía
            `email`.
          example: '28455901'
        first_name:
          type: string
          description: Nombre del operador
          example: Carla
        last_name:
          type: string
          description: Apellido del operador
          example: Gómez
        phone:
          type: string
          description: Número de teléfono del operador, con código de país.
          example: '+541164593678'
      required:
        - email
        - dni
    ApplyBonusDto:
      type: object
      properties:
        applyLevelPointsBonus:
          type: boolean
          description: Aplicar bonificación de puntos por nivel
          example: true
        applyPointRulesBonus:
          type: boolean
          description: Aplicar bonificación de puntos por reglas de puntos
          example: true
      required:
        - applyLevelPointsBonus
        - applyPointRulesBonus
    CreateGiftCardOfferDto:
      type: object
      properties:
        clientMail:
          type: string
          description: Correo electrónico del cliente
          example: exampleMail@tiendadepuntos.com
        points:
          description: Puntos a entregar
          allOf:
            - $ref: '#/components/schemas/PointsDto'
        applyBonus:
          description: Aplicar bonificaciones de puntos
          allOf:
            - $ref: '#/components/schemas/ApplyBonusDto'
      required:
        - clientMail
        - points
        - applyBonus
    PointsDto:
      type: object
      properties:
        selectedRewardType:
          type: string
          description: Tipo de recompensa seleccionada por el operador
          enum:
            - points
            - money
            - items
            - none
          example: points
        amount:
          type: number
          description: Cantidad de dinero que se ingreso para convertir a puntos
          example: 50
        pointItemsIds:
          description: id de los items de puntos
          type: array
          items:
            type: number
        pointsToGive:
          type: number
          description: Cantidad de puntos a otorgar como recompensa
          example: 100
        reason:
          type: string
          description: Razón o motivo de la recompensa
          example: Se entregan 500 puntos por comprar un cafe cuyo valor fue de $50
      required:
        - selectedRewardType
        - amount
        - pointItemsIds
        - pointsToGive
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        API key del comercio. Se genera desde el panel de Tienda de Puntos, en
        Configuración → Integraciones.

````