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

# Crear deuda con debito automatico

> Crea una deuda cuya cobranza automatica la gobierna el medio de pago default del usuario. La identidad del producto viaja por productId o externalProductId (uno solo). productName y paymentMethod no se aceptan. La deuda siempre se crea; autopay/autopayReason indican si el cobro automatico quedo activo y por que.



## OpenAPI

````yaml /api-reference/openapi.yaml post /v2/debts
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:
  /v2/debts:
    post:
      tags:
        - Debts
      summary: Crear deuda con debito automatico
      description: >-
        Crea una deuda cuya cobranza automatica la gobierna el medio de pago
        default del usuario. La identidad del producto viaja por productId o
        externalProductId (uno solo). productName y paymentMethod no se aceptan.
        La deuda siempre se crea; autopay/autopayReason indican si el cobro
        automatico quedo activo y por que.
      operationId: createDebtV2
      parameters:
        - name: Idempotency-Key
          in: header
          schema:
            type: string
          description: >-
            Solo en la deuda suelta. Se usa como externalRequestId de la deuda,
            y el debtId resultante es `debt_<key>`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/DebtV2CreateRequest'
                - $ref: '#/components/schemas/DebtV2BatchCreateRequest'
      responses:
        '201':
          description: >-
            Deuda creada (con o sin cobro automatico). En modo lote, todas las
            deudas fueron creadas y `data` es un array en el orden enviado.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    oneOf:
                      - $ref: '#/components/schemas/DebtV2'
                      - type: array
                        items:
                          $ref: '#/components/schemas/DebtV2'
                  requestId:
                    type: string
        '207':
          description: >-
            Solo en modo lote: algunas deudas se crearon y otras fallaron.
            `data` trae las creadas y `errors` los items fallidos con su `index`
            y `externalRequestId`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DebtV2BatchResult'
        '400':
          description: >-
            Validacion fallida: cualquier campo fuera del contrato (incluidos
            autopay, paymentMethods, productName y paymentMethod), productId y
            externalProductId juntos, productId con formato distinto de
            `prd_<número>`, booleanos no literales, description no string,
            additionalData no objeto o con claves reservadas, array JSON en la
            raíz. En modo lote, un solo item invalido rechaza el lote completo
            (cada `details[]` trae `index`, `externalRequestId`, `field` e
            `issue`) y no se crea ninguna deuda.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Producto referenciado inexistente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: externalRequestId ya existe
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: >-
            Error de resolucion o violacion de regla de negocio (por ejemplo,
            externalClientId que no corresponde a un contacto: 422
            BUSINESS_RULE_VIOLATION, no se crea nada; para crear el contacto en
            el mismo request usa contactData). En modo lote, ninguna deuda pudo
            crearse: el body es un DebtV2BatchResult con `data` vacio y todos
            los items en `errors`.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/Error'
                  - $ref: '#/components/schemas/DebtV2BatchResult'
components:
  schemas:
    DebtV2CreateRequest:
      type: object
      required:
        - amount
        - dueDate
      description: >-
        Contrato de creación de deuda con la identidad de producto por
        referencia (productId o externalProductId), nunca por nombre. No incluye
        paymentMethods: en v2 la deuda no declara medio de pago, el cobro
        automático lo gobierna el default del usuario. productName tampoco se
        acepta (400 INVALID_REQUEST): los productos nunca se crean desde este
        endpoint. productId y externalProductId son mutuamente excluyentes.
        Campos aceptados: externalClientId, contactData, amount, currency,
        dueDate, description, allowOverduePayment, allowPartialPayments,
        additionalData, productId, externalProductId. Cualquier otro campo
        (incluidos autopay, paymentMethods, productName y paymentMethod)
        devuelve 400 con el campo en `details[].field`.
      properties:
        externalClientId:
          type: string
          description: >
            Identificador del deudor (externalClientId o contactData, no ambos).
            Si contactData no se envía, este campo es obligatorio.
        contactData:
          $ref: '#/components/schemas/ContactInput'
          description: >
            Datos completos del deudor inline. Si se envía, externalClientId no
            se permite. Alternativa a externalClientId.
        amount:
          type: number
          minimum: 0.01
          multipleOf: 0.01
          description: Monto en pesos (hasta 2 decimales, mínimo 0.01)
        currency:
          $ref: '#/components/schemas/Currency'
          description: >-
            Código de moneda ISO 4217 (MXN, ARS, PEN, COP, CLP, USD). Default
            MXN.
        dueDate:
          type: string
          format: date
          description: >-
            Fecha de vencimiento (YYYY-MM-DD), fecha de calendario válida. No se
            valida que sea futura.
        description:
          type: string
          description: >-
            Descripción visible al deudor (opcional). Debe ser string (si no,
            400).
        allowOverduePayment:
          type: boolean
          default: true
          description: >-
            Si false, no se puede pagar después del dueDate. Solo acepta
            `true`/`false` literal: `"true"`, `1`, etc. devuelven 400 con
            `details[]`.
        allowPartialPayments:
          type: boolean
          default: true
          description: >-
            Habilita pagos parciales. Solo acepta `true`/`false` literal:
            `"true"`, `1`, etc. devuelven 400 con `details[]`.
        additionalData:
          $ref: '#/components/schemas/AdditionalData'
        productId:
          type: string
          pattern: ^prd_\d+$
          description: >-
            ID del producto en Tapi, formato `prd_<número>`. Otro formato
            devuelve 400 INVALID_REQUEST. Excluyente con externalProductId.
        externalProductId:
          type: string
          description: >-
            Tu identificador del producto. Excluyente con productId. No admite
            los caracteres `'`, `"`, `\`, `;` (400).
    DebtV2BatchCreateRequest:
      type: object
      required:
        - debts
      description: >-
        Lote de hasta 50 deudas en un solo request. Cada item es una deuda del
        contrato suelto mas `externalRequestId`, obligatorio y unico en el lote
        (la idempotencia de cada deuda; el header Idempotency-Key no aplica en
        este modo). Si el body trae `debts`, solo se acepta `debts` en el nivel
        superior: cualquier otro campo devuelve 400. Un array JSON en la raíz
        (sin envolver en `{"debts": [...]}`) también devuelve 400. Si un item es
        inválido, cada entrada de `details[]` trae `index`, `externalRequestId`,
        `field` e `issue`. Las deudas se crean de forma independiente: la que
        falla no frena a las demas.
      properties:
        debts:
          type: array
          minItems: 1
          maxItems: 50
          items:
            $ref: '#/components/schemas/DebtV2BatchItem'
    DebtV2:
      allOf:
        - $ref: '#/components/schemas/Debt'
        - type: object
          properties:
            autopay:
              type: boolean
              description: >-
                true si la adhesion quedo solicitada y la deuda se debita
                automaticamente. false nunca es un error: la deuda existe y se
                paga por su paymentUrl.
            autopayReason:
              type: string
              nullable: true
              enum:
                - DEFAULT_PAYMENT_METHODS_DISABLED_FOR_COMPANY
                - NO_DEFAULT_PAYMENT_METHOD
                - DEFAULT_PAYMENT_METHOD_TYPE_NOT_SUPPORTED
                - COMPANY_WITHOUT_PRODUCT_MODALITY
                - ADHESION_EVENT_FAILED
                - NO_PRODUCT
              description: >-
                Motivo cuando autopay es false. Ausente (no null) cuando autopay
                es true. NO_PRODUCT: la deuda no referencia un producto; el
                débito automático se gestiona por producto, así que no se
                evalúa.
            autopayInstrument:
              type: object
              nullable: true
              allOf:
                - $ref: '#/components/schemas/PaymentMethodRef'
    DebtV2BatchResult:
      type: object
      properties:
        data:
          type: array
          description: Deudas creadas, en el orden en que fueron enviadas.
          items:
            $ref: '#/components/schemas/DebtV2'
        errors:
          type: array
          description: Items que no pudieron crearse.
          items:
            type: object
            properties:
              index:
                type: integer
                description: Posicion del item en el array `debts` enviado.
              externalRequestId:
                type: string
              code:
                type: string
                description: Mismo catalogo de codigos que el endpoint de una deuda.
              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
              description: >-
                Código de error estándar. BAD_REQUEST se usa cuando tu cliente
                tiene varias companies y no se envía `companyCode`.
                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).
            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 el modo lote de POST /v2/debts. Posición del item
                      en `debts`.
                  externalRequestId:
                    type: string
                    description: >-
                      Solo en el modo lote de POST /v2/debts. externalRequestId
                      del item.
        requestId:
          type: string
          pattern: ^req_[a-zA-Z0-9]+$
          description: ID único del request para trazabilidad
    ContactInput:
      type: object
      required:
        - externalClientId
        - name
        - email
      properties:
        externalClientId:
          type: string
          description: Llave natural del contacto. Única por organización.
        name:
          type: string
          description: Nombre del deudor
        email:
          type: string
          format: email
          description: Email del deudor (requerido)
        phones:
          type: array
          description: Lista de teléfonos del deudor (opcional)
          items:
            $ref: '#/components/schemas/Phone'
    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.
    AdditionalData:
      type: object
      additionalProperties: true
      description: >-
        Datos adicionales propios del cliente. Debe ser un objeto plano; un
        escalar (string, número o booleano) devuelve 400 INVALID_REQUEST. No
        puede usar claves reservadas, que TapiPay escribe internamente:
        `description`, `paymentMethods`, `productName`, `externalProductId`,
        `debtExpirationDate`, `overduePayment`, `allowPartialPayments`,
        `recurringDebt`, `debtReference`. Si aparece alguna, la API responde 400
        INVALID_REQUEST con `details[].field = additionalData`. Se persiste en
        la deuda (en ligas de pago, en la deuda asociada al link; en
        suscripciones, en las deudas generadas).
      example:
        source: crm
        folio: F-2026-0042
    DebtV2BatchItem:
      allOf:
        - $ref: '#/components/schemas/DebtV2CreateRequest'
        - type: object
          required:
            - externalRequestId
          properties:
            externalRequestId:
              type: string
              description: >-
                Identificador idempotente de esta deuda, unico dentro del lote.
                Cumple el mismo rol que el header Idempotency-Key en la deuda
                suelta: repetirlo devuelve la deuda existente sin duplicarla.
    Debt:
      type: object
      required:
        - debtId
        - externalRequestId
        - status
        - amount
        - amountPaid
        - currency
        - dueDate
        - allowOverduePayment
        - allowPartialPayments
        - autopay
        - 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: intentar crear con el mismo
            `externalRequestId` activo devuelve 409 RESOURCE_CONFLICT (no es
            idempotencia clásica con replay, sino rechazo de duplicado).
        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)
        currency:
          $ref: '#/components/schemas/Currency'
        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
        autopay:
          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)
    PaymentMethodRef:
      type: object
      description: >-
        Referencia no sensible al medio de pago del usuario. Nunca viaja el dato
        real del instrumento (CLABE/PAN), solo su referencia opaca.
      properties:
        paymentMethodId:
          type: string
          description: Referencia opaca al instrumento en el servicio de pago.
          example: 8b2e2a1e-1111-4222-8333-444455556666
        paymentMethodType:
          type: string
          enum:
            - BANK_ACCOUNT
            - YUNO
          description: >-
            BANK_ACCOUNT ejecuta debito automatico; YUNO (tarjeta tokenizada) se
            registra pero no debita en esta version.
        isDefault:
          type: boolean
        status:
          type: string
          enum:
            - ACTIVE
            - INACTIVE
        createdAt:
          type: string
          format: date-time
          nullable: true
          description: Timestamp de registro (ISO 8601 en UTC con milisegundos y Z)
    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
    Phone:
      type: object
      required:
        - number
        - type
      properties:
        number:
          type: string
          description: Número de teléfono. Ej +5215512345678
        type:
          $ref: '#/components/schemas/PhoneType'
        primary:
          type: boolean
          default: false
          description: >
            Indica si es el número telefónico principal. Opcional (default:
            false).
        description:
          type: string
          description: >
            Descripción o etiqueta del teléfono. Opcional (default: type cuando
            no se especifica).
    DebtStatus:
      type: string
      enum:
        - PENDING
        - PARTIALLY_PAID
        - PAID
        - OVERDUE
        - CANCELLED
    PaymentMethod:
      type: string
      enum:
        - CASH
        - CARD
        - TRANSFER
        - WALLET
        - BANK_TRANSFER
    ContactWithInline:
      type: object
      required:
        - contactId
        - externalClientId
        - createdInline
      properties:
        contactId:
          type: string
          pattern: ^con_[a-zA-Z0-9]+$
          description: >-
            ID interno del contacto (prefijo con_, también cuando el contacto se
            crea inline)
        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.
    PhoneType:
      type: string
      enum:
        - MAIN
        - MOBILE
        - WORK
        - RELATIVE
        - OTHER
  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...

````