> ## 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.

# Canjear un premio

> Descuenta los puntos al cliente y genera un canje pendiente de entrega, con su código.

El canje **no queda entregado**: para eso, después llamá a `POST /external/purchase/redeem/{code}` con el `code` que devuelve esta llamada. La API pública no puede registrar un canje como entregado en un solo paso, porque esa operación exige un operador identificado.

Validaciones que pueden fallar con 409: puntos insuficientes, premio deshabilitado, sin stock, nivel del cliente insuficiente o tope de canjes alcanzado.



## OpenAPI

````yaml /api-reference/openapi.json post /external/exchange/product
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/exchange/product:
    post:
      tags:
        - Canjes
      summary: Canjear un premio
      description: >-
        Descuenta los puntos al cliente y genera un canje pendiente de entrega,
        con su código.


        El canje **no queda entregado**: para eso, después llamá a `POST
        /external/purchase/redeem/{code}` con el `code` que devuelve esta
        llamada. La API pública no puede registrar un canje como entregado en un
        solo paso, porque esa operación exige un operador identificado.


        Validaciones que pueden fallar con 409: puntos insuficientes, premio
        deshabilitado, sin stock, nivel del cliente insuficiente o tope de
        canjes alcanzado.
      operationId: ExternalExchangeController_exchangeProduct
      parameters:
        - name: x-branch-id
          in: header
          description: >-
            ID de la sucursal en la que se registra la operación. Si se omite,
            la operación queda asociada al comercio sin sucursal.
          required: false
          schema:
            type: integer
            example: 1972
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExternalExchangeProductDto'
      responses:
        '201':
          description: >-
            Canje creado en estado `pending`. El envelope trae `status: 200`
            aunque el código HTTP sea 201.
          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:
                    $ref: '#/components/schemas/ExternalPurchaseOutputDto'
                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
        '404':
          description: El cliente o la oferta del premio no existen en este comercio.
          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: Client not found
                  errors:
                    type: array
                    nullable: true
                    items:
                      type: object
                  data:
                    type: object
                    nullable: true
        '409':
          description: >-
            El canje no se pudo realizar: puntos insuficientes, premio no
            disponible, sin stock, nivel insuficiente o límite de canjes
            alcanzado. El `message` indica el caso.
          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: El producto no se encuentra disponible.
                  errors:
                    type: array
                    nullable: true
                    items:
                      type: object
                  data:
                    type: object
                    nullable: true
      security:
        - ApiKeyAuth: []
components:
  schemas:
    ExternalExchangeProductDto:
      type: object
      properties:
        productOfferId:
          type: number
          description: >-
            ID de la oferta del premio a canjear. Sale del array `offers` de
            `GET /external/products`, eligiendo la que corresponde al nivel del
            cliente.
          example: 4412
        clientId:
          type: number
          description: >-
            ID del cliente que canjea. Se obtiene de `GET
            /external/clients/search`.
          example: 84213
      required:
        - productOfferId
        - clientId
    ExternalPurchaseOutputDto:
      type: object
      properties:
        id:
          type: number
          description: ID del canje.
          example: 45012
        code:
          type: string
          description: >-
            Código que presenta el cliente para retirar el premio. Es el que se
            usa en `POST /external/purchase/redeem/{code}`.
          example: A1B2C3
          nullable: true
        status:
          type: string
          description: Estado del canje. Los canjes creados por la API nacen en `pending`.
          enum:
            - pending
            - canceled
            - delivered
          example: pending
        type:
          type: string
          description: Si el canje fue por un premio o por dinero.
          enum:
            - product
            - money
        points:
          type: number
          description: Puntos debitados al cliente.
          example: 500
        moneyAmount:
          type: number
          description: Monto en pesos. Solo en canjes por dinero.
          example: null
          nullable: true
        product:
          type: object
          description: Premio canjeado. `null` en canjes por dinero.
          example:
            id: 771
            name: Café gratis
          nullable: true
        client:
          type: object
          description: Cliente que canjeó.
          example:
            id: 84213
            firstName: Juan Manuel
            lastName: Pérez
        branch:
          type: object
          description: Sucursal donde se registró el canje.
          example:
            id: 1972
            name: Sucursal Centro
          nullable: true
        createdAt:
          format: date-time
          type: string
          example: '2026-07-28T18:05:00.000Z'
        updatedAt:
          format: date-time
          type: string
          example: '2026-07-28T18:05:00.000Z'
      required:
        - id
        - code
        - status
        - type
        - points
        - moneyAmount
        - product
        - client
        - branch
        - createdAt
        - updatedAt
  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.

````