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

# Cancelar deudas en lote

> Cancela hasta 50 deudas en un request. Cada deuda sigue la misma regla que POST /debts/{id}/cancel y se resuelve sola: la que falla no frena a las demás. Una deuda que ya estaba cancelada cuenta como cancelada, así que reintentar el mismo lote es seguro. Las canceladas vienen en `data` y el resto en `errors`, con su `index` y el `id` tal como lo enviaste.



## OpenAPI

````yaml /api-reference/openapi.yaml post /debts/cancel
openapi: 3.0.0
info:
  title: TapiPay API
  version: 1.0.0
  description: >
    API pública de la plataforma de cobranzas TapiPay.  Ofrece un contrato
    limpio y desacoplado del modelo interno de la plataforma, permitiendo a los
    clientes crear y gestionar cobros (deudas puntuales,  suscripciones
    recurrentes, links de pago, productos y contactos)  sin conocer detalles
    internos de Company, Modalities o GenerationData.
  contact:
    name: TapiPay Team
    url: https://www.tapipay.com
  license:
    name: Proprietary
servers:
  - url: https://tapipay-facade.homo.tapila.cloud
    description: Homologación
security:
  - apiKeyAuth: []
    tapiAuth: []
paths:
  /debts/cancel:
    post:
      tags:
        - Debts
      summary: Cancelar deudas en lote
      description: >-
        Cancela hasta 50 deudas en un request. Cada deuda sigue la misma regla
        que POST /debts/{id}/cancel y se resuelve sola: la que falla no frena a
        las demás. Una deuda que ya estaba cancelada cuenta como cancelada, así
        que reintentar el mismo lote es seguro. Las canceladas vienen en `data`
        y el resto en `errors`, con su `index` y el `id` tal como lo enviaste.
      operationId: cancelDebtsBatch
      parameters:
        - $ref: '#/components/parameters/CompanyCode'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DebtCancelBatchRequest'
      responses:
        '200':
          description: >-
            Todas las deudas quedaron canceladas. `data` las trae en el orden
            enviado y no viene `errors`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DebtCancelBatchResult'
        '207':
          description: >-
            Algunas deudas se cancelaron y otras no. `data` trae las canceladas
            y `errors` el resto.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DebtCancelBatchResult'
        '400':
          description: >-
            Validación fallida, y no se canceló ninguna deuda: falta `debts` o
            no es un array con items, trae más de 50, hay otro campo en el nivel
            superior, o un item es inválido (sin `id`, `id` que no es string o
            tiene espacios al principio o al final, solo el prefijo `debt_`,
            repetido o con otros campos). Cada `details[]` trae `index`, `field`
            e `issue`. También BAD_REQUEST si tu cliente tiene varias companies
            y no envías companyCode.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          description: >-
            No se canceló ninguna deuda y ningún item trae INTERNAL_ERROR (por
            ejemplo, no existen, no se pueden cancelar en su estado o no se
            pudieron consultar): el body es un DebtCancelBatchResult con `data`
            vacío y todos los items en `errors`. Un companyCode que no es de tu
            cliente responde 422 RESOLUTION_ERROR con el envelope de error.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/DebtCancelBatchResult'
                  - $ref: '#/components/schemas/Error'
        '500':
          description: >-
            Ninguna deuda se confirmó como cancelada y al menos un item trae
            INTERNAL_ERROR, una falla de nuestro lado: no significa que no se
            hayan cancelado. Reintenta el mismo lote; una deuda que ya quedó
            cancelada vuelve en `data`. El body es un DebtCancelBatchResult con
            los items en `errors`, o el envelope de error si la falla fue de
            todo el request.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/DebtCancelBatchResult'
                  - $ref: '#/components/schemas/Error'
components:
  parameters:
    CompanyCode:
      name: companyCode
      in: query
      required: false
      schema:
        type: string
        example: MX-S-67890
      description: >-
        Solo si tu cliente tiene varias companies: indica en cuál operas. En
        POST y PATCH también se puede enviar en el cuerpo (el cuerpo tiene
        prioridad). Si tu cliente tiene varias companies y no lo envías, 400
        BAD_REQUEST; si no es una company de tu cliente, 422 RESOLUTION_ERROR
        (403 en /v2/debts/orders).
  schemas:
    DebtCancelBatchRequest:
      type: object
      required:
        - debts
      description: >-
        Lote de hasta 50 deudas a cancelar en un solo request. En el nivel
        superior solo se acepta `debts` (y `companyCode`): cualquier otro campo
        devuelve 400. Cada item solo lleva `id`. Si un item es inválido no se
        cancela ninguna deuda, y cada entrada de `details[]` trae `index`,
        `field` e `issue`.
      properties:
        debts:
          type: array
          minItems: 1
          maxItems: 50
          items:
            type: object
            required:
              - id
            properties:
              id:
                type: string
                example: debt_COLE-OCT-00042
                description: >-
                  debtId (`debt_...`) o externalRequestId sin prefijo, igual que
                  en POST /debts/{id}/cancel. Sin espacios al principio ni al
                  final, y sin repetir dentro del lote: `debt_A` y `A` son la
                  misma deuda.
    DebtCancelBatchResult:
      type: object
      properties:
        data:
          type: array
          description: >-
            Deudas canceladas, en el orden en que fueron enviadas, incluidas las
            que ya estaban canceladas. `paymentUrl` viene en null: una deuda
            cancelada ya no se paga.
          items:
            $ref: '#/components/schemas/Debt'
        errors:
          type: array
          description: >-
            Deudas que no se cancelaron o cuya cancelación no se pudo confirmar.
            Solo viene si hay alguna.
          items:
            type: object
            properties:
              index:
                type: integer
                description: Posición del item en el array `debts` enviado.
              id:
                type: string
                description: El `id` tal como lo enviaste.
              code:
                type: string
                description: >-
                  RESOURCE_NOT_FOUND si la deuda no existe.
                  BUSINESS_RULE_VIOLATION si no se puede cancelar en su estado
                  actual, también si se pagó o entró en débito justo antes de la
                  cancelación. RESOURCE_CONFLICT si se pidió la cancelación pero
                  la deuda sigue activa: el reintento la cancela. RATE_LIMITED
                  si no se pudo consultar la deuda: no se pidió su cancelación.
                  INTERNAL_ERROR si falló de nuestro lado: la cancelación no se
                  llegó a pedir o no se pudo confirmar, y `message` dice cuál.
              message:
                type: string
        requestId:
          type: string
    Error:
      type: object
      required:
        - error
        - requestId
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - INVALID_REQUEST
                - BAD_REQUEST
                - RESOURCE_NOT_FOUND
                - RESOURCE_CONFLICT
                - RESOLUTION_ERROR
                - BUSINESS_RULE_VIOLATION
                - RATE_LIMITED
                - INTERNAL_ERROR
                - PARTIAL_UPDATE
                - NO_ACTIVE_PAYMENT_METHOD
                - COMPANY_WITHOUT_PRODUCT_MODALITY
                - CONTACT_NOT_FOUND
                - COMPANY_WITHOUT_DEBT_ID_MODALITY
                - PAYMENT_METHODS_NOT_ENABLED
                - ORDER_NOT_CREATED
                - PAYMENT_METHOD_NOT_AVAILABLE
                - PAYMENT_METHOD_CASCADE_FAILED
                - PAYMENT_METHOD_NOT_DELETED
              description: >-
                Código de error estándar. BAD_REQUEST se usa cuando tu cliente
                tiene varias companies y no se envía `companyCode`.
                NO_ACTIVE_PAYMENT_METHOD y COMPANY_WITHOUT_PRODUCT_MODALITY
                aparecen en POST /adhesions (el primero también en POST
                /v2/debts/orders). CONTACT_NOT_FOUND,
                COMPANY_WITHOUT_DEBT_ID_MODALITY, PAYMENT_METHODS_NOT_ENABLED,
                ORDER_NOT_CREATED y PAYMENT_METHOD_NOT_AVAILABLE solo aparecen
                en POST /v2/debts/orders. PAYMENT_METHOD_CASCADE_FAILED y
                PAYMENT_METHOD_NOT_DELETED solo aparecen en DELETE
                /payment-methods/{externalPaymentMethodId}. PARTIAL_UPDATE solo
                aparece en el 207 de PATCH /subscriptions/{id}: la suscripción
                se actualizó, pero alguna de sus deudas no. Los 401 y 403 los
                emite el API Gateway y no usan este envelope (ver GatewayError).
                Excepción: en /v2/debts/orders, RESOLUTION_ERROR responde 403
                con este envelope.
            message:
              type: string
              description: >-
                Mensaje legible del error. Es informativo: no lo parsees, usa
                `code` y `details[].field`.
            details:
              type: array
              description: >-
                Array de detalles adicionales (validaciones por campo, etc.). Un
                JSON malformado devuelve `[{ "field": "body", "issue": "Invalid
                JSON syntax" }]`.
              items:
                type: object
                properties:
                  field:
                    type: string
                    description: Campo que falló la validación
                  issue:
                    type: string
                    description: Descripción del problema
                  index:
                    type: integer
                    description: >-
                      Solo en los lotes de POST /v2/debts y POST /debts/cancel.
                      Posición del item en `debts`.
                  externalRequestId:
                    type: string
                    description: >-
                      Solo en el modo lote de POST /v2/debts. externalRequestId
                      del item.
                  productId:
                    type: string
                    nullable: true
                    description: >-
                      Solo en DELETE /payment-methods/{externalPaymentMethodId}.
                      Producto de la adhesión.
                  outcome:
                    type: string
                    enum:
                      - DEACTIVATED
                      - FAILED
                    description: >-
                      Solo en PAYMENT_METHOD_CASCADE_FAILED. Si la adhesión se
                      dio de baja o no.
        requestId:
          type: string
          pattern: ^req_[a-zA-Z0-9]+$
          description: ID único del request para trazabilidad
    Debt:
      allOf:
        - $ref: '#/components/schemas/DebtCommon'
        - type: object
          required:
            - autopay
          properties:
            autopay:
              type: boolean
              description: >-
                Flag propio de la deuda. No indica que exista una adhesion
                vigente: la adhesion se gestiona aparte, por producto.
    GatewayError:
      type: object
      description: >-
        Respuesta de error emitida por el API Gateway, antes de llegar al
        servicio. No lleva el envelope `error` ni `requestId`. Casos: token
        faltante o inválido en `x-authorization-token` devuelve `401 {"message":
        "Unauthorized"}`; `x-api-key` faltante o inválida devuelve `403
        {"message": "Forbidden"}`; token válido sin permiso sobre el recurso
        devuelve `403 {"Message": "User is not authorized to access this
        resource with an explicit deny"}`. Ojo al tipar: en este último caso el
        campo viene con mayúscula (`Message`), no `message`.
      properties:
        message:
          type: string
          example: Unauthorized
        Message:
          type: string
          description: Solo en el 403 de explicit deny (con mayúscula).
          example: User is not authorized to access this resource with an explicit deny
    DebtCommon:
      type: object
      required:
        - debtId
        - externalRequestId
        - status
        - amount
        - amountPaid
        - currency
        - dueDate
        - allowOverduePayment
        - allowPartialPayments
        - createdAt
      properties:
        debtId:
          type: string
          pattern: ^debt_[a-zA-Z0-9_-]+$
          example: debt_COLE-OCT-00042
          description: >-
            ID público de la deuda: `debt_` + externalRequestId (ej.
            `debt_COLE-OCT-00042`). En la deuda de una liga de pago es
            `debt_pl-debt-` + externalPaymentLinkId.
        externalRequestId:
          type: string
          description: >
            Llave única permanente de la deuda (sin TTL). Igual al header
            `Idempotency-Key` si se envió, o generada por el backend (UUID). Es
            única por organización: crear con un `externalRequestId` ya usado no
            crea una segunda deuda. Para ver la deuda guardada, consulta GET
            /debts/{id}.
        status:
          $ref: '#/components/schemas/DebtStatus'
        amount:
          type: number
          description: Monto total en pesos (hasta 2 decimales)
        amountPaid:
          type: number
          description: >-
            Monto pagado acumulado en pesos (hasta 2 decimales). En una deuda
            OPEN es la suma de los pagos recibidos.
        currency:
          $ref: '#/components/schemas/Currency'
        amountType:
          type: string
          enum:
            - CLOSED
            - OPEN
          description: >-
            CLOSED: el monto exacto es el que se cobra. OPEN: el deudor elige
            cuánto paga dentro de un rango.
        dueDate:
          type: string
          format: date
          description: Fecha de vencimiento (YYYY-MM-DD)
        description:
          type: string
          description: >-
            Descripción. Se devuelve en la creación y en las lecturas (GET
            /debts/{id}, GET /debts) cuando la deuda tiene una; si no, el campo
            no viene (ausente, no null).
        allowOverduePayment:
          type: boolean
        allowPartialPayments:
          type: boolean
        paymentMethods:
          type: array
          items:
            $ref: '#/components/schemas/PaymentMethod'
          description: Medios de pago habilitados para esta deuda
        paymentUrl:
          type: string
          format: uri
          nullable: true
          description: >-
            URL pública del portal de pago. Es una URL opaca: no la parsees ni
            construyas a mano.

            Puede llevar ?externalRequestId=<externalRequestId> para abrir
            directo esta deuda.

            Ej:
            `https://app.tapipay.la/s/acme-corp/portal/aB3dE5fG7h/debts/payment-methods?externalRequestId=COLE-OCT-00042`.

            Puede ser null si falla la integración con el portal.
        contact:
          $ref: '#/components/schemas/ContactWithInline'
          description: Datos del contacto resuelto o creado inline
        product:
          $ref: '#/components/schemas/ProductWithInline'
          description: Datos del producto si se asoció
        createdAt:
          type: string
          format: date-time
          description: >-
            Timestamp de creación (ISO 8601 en UTC con milisegundos y Z, ej.
            2026-09-17T10:00:00.000Z)
    DebtStatus:
      type: string
      enum:
        - PENDING
        - PARTIALLY_PAID
        - PAID
        - OVERDUE
        - CANCELLED
    Currency:
      type: string
      enum:
        - MXN
        - ARS
        - PEN
        - COP
        - CLP
        - USD
      default: MXN
      description: >
        Código de moneda ISO 4217. Whitelist de monedas aceptadas por la API.
        Default MXN. Si se envía un valor fuera de este whitelist, la API
        devuelve 400 INVALID_REQUEST.
    PaymentMethod:
      type: string
      enum:
        - CASH
        - CARD
        - TRANSFER
        - WALLET
        - BANK_TRANSFER
    ContactWithInline:
      type: object
      required:
        - contactId
        - externalClientId
        - createdInline
      properties:
        contactId:
          type: string
          nullable: true
          pattern: ^con_[a-zA-Z0-9]+$
          description: >-
            ID interno del contacto (prefijo con_, también cuando el contacto se
            crea inline). Puede venir en null si el contacto recién creado
            todavía no se pudo leer; consúltalo después con GET /contacts.
        externalClientId:
          type: string
          description: Llave natural del contacto
        createdInline:
          type: boolean
          description: >
            Indica si el contacto fue creado inline en esta operación. NOTA:
            Este es un embedded thin contact (no incluye
            name/email/phones/active/updatedAt del contacto completo). Para
            obtener datos completos del contacto, consultá GET /contacts/{id}.
    ProductWithInline:
      type: object
      required:
        - productId
        - name
        - active
        - createdInline
      properties:
        productId:
          type: string
          pattern: ^prd_\d+$
          description: ID público del producto, con prefijo `prd_` (ej. `prd_12985`).
        name:
          type: string
          description: Nombre del producto
        active:
          type: boolean
          description: Estado del producto
        createdInline:
          type: boolean
          description: >-
            Indica si el producto fue creado inline en esta operación (solo
            posible via productName en suscripciones y ligas de pago). En deudas
            es siempre false: el producto debe existir de antemano. Se conserva
            en las lecturas.
  responses:
    Unauthorized:
      description: >-
        No autenticado. Lo emite el API Gateway cuando falta o es inválido el
        token en `x-authorization-token`. Body `{"message": "Unauthorized"}`,
        sin envelope `error` ni `requestId`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/GatewayError'
          example:
            message: Unauthorized
    Forbidden:
      description: >-
        Prohibido. Lo emite el API Gateway, sin envelope `error` ni `requestId`.
        Si falta o es inválida la `x-api-key`, el body es `{"message":
        "Forbidden"}`. Si el token es válido pero no tiene permiso sobre el
        recurso, el body es `{"Message": "User is not authorized to access this
        resource with an explicit deny"}` (campo con mayúscula).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/GatewayError'
          examples:
            apiKey:
              summary: x-api-key faltante o inválida
              value:
                message: Forbidden
            explicitDeny:
              summary: Token sin permiso sobre el recurso
              value:
                Message: >-
                  User is not authorized to access this resource with an
                  explicit deny
  securitySchemes:
    apiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >
        API key del API Gateway de Tapi. Requerida en todas las operaciones.
        Distinta por ambiente (desarrollo, homologación, producción).
    tapiAuth:
      type: apiKey
      in: header
      name: x-authorization-token
      description: >
        Token TAPI JWT. Requerido en todas las operaciones. Se envía sin prefijo
        "Bearer". Ej: x-authorization-token: eyJhbGci...

````

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