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

# Emitir un comprobante especial

> Emite un e-CF 41 (compras a proveedores que no emiten comprobantes), 43 (gastos menores), 46 (exportaciones) o 47 (pagos al exterior), lo firma y lo envía a la DGII. La respuesta suele llegar con `status: pending`; el estado se actualiza solo: recíbelo con un webhook o consúltalo con `GET /v1/special-documents/{id}`.

**Límite mensual:** si alcanzaste el límite mensual de este tipo de e-CF responde `429` **sin** `Retry-After` (el `429` por exceso de solicitudes sí lo trae); consulta tu consumo con `GET /v1/limits`. Consultar, descargar y anular siguen funcionando, y reintentar un documento ya emitido no vuelve a contar. Ese `429` no se guarda: cuando se amplíe el límite puedes reintentar con la misma `Idempotency-Key`.



## OpenAPI

````yaml /api-reference/ecf.json post /v1/special-documents
openapi: 3.0.0
info:
  title: AC-PAY Facturación Electrónica API
  description: >

    API para emitir y consultar comprobantes fiscales electrónicos (e-CF) ante
    la DGII de República Dominicana.


    ## Autenticación

    Envía tu llave en cada solicitud: `Authorization: Bearer <token>`.

    Las llaves tienen el ambiente en el prefijo: `acpay_test_...` (pruebas, sin
    validez fiscal) o `acpay_live_...` (producción).

    Cada ambiente tiene su propia URL base; una llave de un ambiente es
    rechazada en el otro con `401`.


    ## Idempotencia

    Todas las operaciones `POST` (emitir, anular, reintentar, notas,
    comprobantes especiales, registrar y probar webhooks) exigen el encabezado
    `Idempotency-Key`. `PATCH` y `DELETE` no la usan: repetirlos deja el mismo
    resultado.

    Reintentar con la misma llave y la misma solicitud durante 24 h devuelve la
    respuesta original con `Idempotent-Replayed: true` sin volver a ejecutarla.

    Usar la misma llave con otra solicitud devuelve `422`; si la primera sigue
    en curso, `409`.

    No se guardan los `401`, `403`, `409`, `429`, los `5xx` ni los `400` de
    validación del cuerpo: después de cualquiera de ellos puedes reintentar con
    la misma llave.


    ## Errores

    Todas las respuestas de error tienen la forma `{ "error": { "status": 400,
    "message": "...", "details": ["..."] } }`.

    `details` aparece solo cuando hay errores por campo.


    ## Límites de uso

    Hay dos límites y ambos responden `429`:

    - **Solicitudes por minuto** de tu llave: el `429` trae `Retry-After`
    (segundos a esperar). Cuando están disponibles se incluyen los encabezados
    `X-RateLimit-*`.

    - **Comprobantes por mes** de cada tipo de e-CF (al emitir): el `429` no
    trae `Retry-After` y su mensaje dice cuándo se renueva. Consulta tu consumo
    con `GET /v1/limits` (sección **Límites**).


    ## Estados y webhooks

    El estado de cada e-CF ante la DGII se actualiza solo, en segundo plano: no
    necesitas consultar en bucle. Registra un webhook (sección **Webhooks**)
    para recibir los cambios de estado; las consultas `GET` siguen actualizando
    el estado al momento si lo necesitas.


    ## Trazabilidad

    Cada respuesta incluye `X-Request-Id` (puedes enviar el tuyo). Inclúyelo al
    reportar un problema.
  version: '1.0'
  contact: {}
servers:
  - url: https://e-cf.acpay.do/sandbox
    description: Sandbox (pruebas)
security: []
tags:
  - name: Facturas electrónicas (e-CF)
    description: ''
  - name: Notas de crédito y débito
    description: ''
  - name: Comprobantes especiales
    description: ''
  - name: Secuencias
    description: ''
  - name: Límites
    description: >

      Tu plan puede fijar un **límite mensual de comprobantes por tipo de e-CF**
      (p. ej. 500 e-CF 31 al mes). Consulta con `GET /v1/limits` cuánto llevas
      emitido y cuánto te queda.


      - Solo aparecen los tipos con límite; un tipo que no aparece no tiene
      límite.

      - El mes se cuenta en hora de República Dominicana y se renueva al inicio
      del mes siguiente (`resetsAt`).

      - Te avisamos por correo (al dueño del comercio y a los usuarios de su
      backoffice) al llegar al **80 %** y al **100 %** del límite de cada tipo,
      una vez por mes.

      - `remaining` nunca es negativo y `percentUsed` es un entero redondeado.

      - En el ambiente de pruebas los límites no se aplican (`enforced: false`).


      **Al alcanzar el límite**, emitir un e-CF de ese tipo (`POST
      /v1/invoices`, `POST /v1/invoices/{id}/credit-notes`, `POST
      /v1/invoices/{id}/debit-notes` y `POST /v1/special-documents`) responde
      `429` **sin** `Retry-After`, con un mensaje como:


      ```json

      { "error": { "status": 429, "message": "Alcanzaste el límite mensual de
      comprobantes 31 (500). Se renueva el 1 de octubre; para ampliarlo contacta
      a AC-PAY." } }

      ```


      No lo confundas con el `429` por exceso de solicitudes por minuto, que sí
      trae `Retry-After` (segundos a esperar). El `429` por límite mensual no se
      resuelve reintentando: espera la renovación o pide ampliarlo.


      - Consultar, descargar y anular documentos sigue funcionando con el límite
      alcanzado.

      - Reintentar un documento ya emitido (`/retry` o la misma
      `Idempotency-Key`) no vuelve a contar.

      - Ningún `429` se guarda como respuesta idempotente: cuando se amplíe el
      límite puedes reintentar con la misma `Idempotency-Key`.
  - name: Webhooks
    description: >

      Registra una URL HTTPS de tu servidor y te avisamos cuando cambia el
      estado de un e-CF ante la DGII, sin que tengas que consultar.


      ## Cómo recibir y verificar los eventos


      Enviamos un `POST` con cuerpo JSON a tu URL:


      ```json

      {
        "event": "ecf.accepted",
        "data": {
          "documentType": "invoice",
          "id": "3f1c2b9e-8a4d-4f6e-9c1a-2b7d5e8f0a13",
          "encf": "E310000000001",
          "ecfType": 31,
          "status": "accepted",
          "statusMessage": null,
          "invoiceId": null,
          "occurredAt": "2026-09-27T14:15:00.000Z"
        },
        "timestamp": "2026-09-27T14:15:01.000Z",
        "deliveryId": "b3d4f5a6-1c2e-4f7a-8b9c-0d1e2f3a4b5c"
      }

      ```


      - `event`: `ecf.accepted`, `ecf.rejected`, `ecf.cancelled` o `test`
      (entregas de `POST /v1/webhooks/{id}/test`, cuyo `data` es `{ "message":
      "..." }`).

      - `data.documentType`: `invoice`, `credit_note`, `debit_note` o
      `special_document`.

      - `data.status` / `data.statusMessage`: el nuevo estado y su detalle (p.
      ej. el motivo del rechazo).

      - `data.invoiceId`: en las notas, el e-CF de venta que modifican; `null`
      en el resto.

      - `data.occurredAt`: cuándo cambió el estado. `timestamp`: cuándo se envió
      esta entrega.


      Encabezados de cada entrega:


      | Encabezado | Contenido |

      | --- | --- |

      | `X-Acpay-Signature` | `sha256=<hex>`: HMAC-SHA256 del cuerpo **crudo**
      con el secreto del webhook. |

      | `X-Acpay-Event` | El mismo `event` del cuerpo. |

      | `X-Acpay-Delivery` | El mismo `deliveryId` del cuerpo. |


      **Verifica cada entrega antes de procesarla:**


      1. Calcula el HMAC-SHA256 del cuerpo tal como llegó (bytes crudos, antes
      de parsear el JSON) con tu secreto y compáralo con `X-Acpay-Signature` en
      tiempo constante. Si no coincide, responde `401` y descártala.

      2. Rechaza la entrega si `timestamp` tiene más de 5 minutos (protección
      contra repeticiones).

      3. Deduplica por `deliveryId`: un reintento trae el mismo `deliveryId`; si
      ya lo procesaste, responde `200` sin repetir el trabajo.


      El secreto solo se devuelve al crear el webhook (`POST /v1/webhooks`). Si
      lo pierdes, borra el webhook y crea otro.


      **Responde `2xx` en menos de 10 segundos** (procesa en segundo plano si
      tardas más). Si no respondes `2xx` a tiempo, reintentamos 1 min, 5 min, 30
      min, 2 h y 24 h después de cada fallo: hasta 6 intentos en total (el
      inicial y 5 reintentos). Puedes ver cada entrega y sus intentos en
      `recentDeliveries` de `GET /v1/webhooks/{id}`.


      **Los eventos son livianos:** para el detalle completo consulta el
      documento según `data.documentType`: `GET /v1/invoices/{id}` (`invoice`),
      `GET /v1/notes/{id}` (`credit_note` y `debit_note`) o `GET
      /v1/special-documents/{id}` (`special_document`).


      Ejemplo de verificación en Node.js (Express):


      ```js

      const crypto = require('crypto');

      const express = require('express');


      const WEBHOOK_SECRET = process.env.ECF_WEBHOOK_SECRET;

      const MAX_AGE_MS = 5 * 60 * 1000;


      const app = express();


      // express.raw: se necesita el cuerpo crudo para verificar la firma.

      app.post('/webhooks/ecf', express.raw({ type: 'application/json' }), (req,
      res) => {
        const expected = Buffer.from(
          'sha256=' + crypto.createHmac('sha256', WEBHOOK_SECRET).update(req.body).digest('hex'),
        );
        const received = Buffer.from(req.get('X-Acpay-Signature') ?? '');
        if (received.length !== expected.length || !crypto.timingSafeEqual(received, expected)) {
          return res.sendStatus(401);
        }

        const payload = JSON.parse(req.body.toString('utf8'));
        const age = Date.now() - Date.parse(payload.timestamp);
        if (!Number.isFinite(age) || Math.abs(age) > MAX_AGE_MS) {
          return res.sendStatus(401);
        }

        // Deduplica por payload.deliveryId y procesa en segundo plano.
        res.sendStatus(200);
      });

      ```
  - name: Estado
    description: ''
paths:
  /v1/special-documents:
    post:
      tags:
        - Comprobantes especiales
      summary: Emitir un comprobante especial
      description: >-
        Emite un e-CF 41 (compras a proveedores que no emiten comprobantes), 43
        (gastos menores), 46 (exportaciones) o 47 (pagos al exterior), lo firma
        y lo envía a la DGII. La respuesta suele llegar con `status: pending`;
        el estado se actualiza solo: recíbelo con un webhook o consúltalo con
        `GET /v1/special-documents/{id}`.


        **Límite mensual:** si alcanzaste el límite mensual de este tipo de e-CF
        responde `429` **sin** `Retry-After` (el `429` por exceso de solicitudes
        sí lo trae); consulta tu consumo con `GET /v1/limits`. Consultar,
        descargar y anular siguen funcionando, y reintentar un documento ya
        emitido no vuelve a contar. Ese `429` no se guarda: cuando se amplíe el
        límite puedes reintentar con la misma `Idempotency-Key`.
      operationId: specialDocumentsCreate
      parameters:
        - name: Idempotency-Key
          in: header
          description: >-
            Identificador único que generas por operación (p. ej. un UUID), de 1
            a 255 caracteres ASCII imprimibles. Si repites la solicitud con la
            misma llave en las siguientes 24 h recibes la respuesta original
            (con `Idempotent-Replayed: true`) sin ejecutarla de nuevo.
          required: true
          schema:
            type: string
            example: 5b0f7c1e-2f7a-4d8e-9d3c-7f1e2a9b4c60
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSpecialDocumentDto'
            examples:
              compras:
                summary: E41 · Compra a un proveedor informal, con retenciones
                value:
                  ecfType: 41
                  description: Compra de plátanos a productor informal
                  total: 11800
                  itbis: 1800
                  counterparty:
                    name: Juan Pérez
                    taxId: '00112345678'
                  itbisWithheld: 1800
                  isrWithheld: 200
                  goodOrService: good
              gastosMenores:
                summary: E43 · Gastos menores (sin contraparte)
                value:
                  ecfType: 43
                  description: Peajes y parqueos de la semana
                  total: 850
              exportaciones:
                summary: E46 · Exportación a un cliente del exterior
                value:
                  ecfType: 46
                  description: Exportación de cacao orgánico
                  total: 590000
                  counterparty:
                    name: Chocolate Makers Inc.
                    foreignId: 98-7654321
                    country: US
                  goodOrService: good
              pagosAlExterior:
                summary: E47 · Pago al exterior con retención de ISR
                value:
                  ecfType: 47
                  description: Licencia anual de software
                  total: 60000
                  counterparty:
                    name: Software Company LLC
                    foreignId: EIN 12-3456789
                    country: US
                  isrWithheld: 16200
                  goodOrService: service
      responses:
        '201':
          description: Comprobante creado y enviado a la DGII.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SpecialDocumentResponse'
          headers:
            X-Request-Id:
              description: >-
                Identificador de la solicitud (el tuyo si enviaste uno válido).
                Inclúyelo al reportar un problema.
              schema:
                type: string
            Idempotent-Replayed:
              description: >-
                Viene con `true` cuando la respuesta es la original de una
                solicitud anterior con la misma `Idempotency-Key`.
              schema:
                type: string
                enum:
                  - 'true'
        '400':
          description: Solicitud inválida (cuerpo, parámetros o Idempotency-Key).
          headers:
            X-Request-Id:
              description: >-
                Identificador de la solicitud (el tuyo si enviaste uno válido).
                Inclúyelo al reportar un problema.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelopeResponse'
              example:
                error:
                  status: 400
                  message: La solicitud no es válida.
                  details:
                    - 'items.0.quantity: debe ser un número entero.'
                    - 'dueDate: es obligatoria cuando paymentType es credit.'
        '401':
          description: >-
            Token ausente, con formato inválido, de otro ambiente, vencido o
            revocado.
          headers:
            X-Request-Id:
              description: >-
                Identificador de la solicitud (el tuyo si enviaste uno válido).
                Inclúyelo al reportar un problema.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelopeResponse'
              example:
                error:
                  status: 401
                  message: Esta llave pertenece a otro ambiente.
        '403':
          description: >-
            La llave no tiene permiso para esta operación o el comercio está
            inactivo.
          headers:
            X-Request-Id:
              description: >-
                Identificador de la solicitud (el tuyo si enviaste uno válido).
                Inclúyelo al reportar un problema.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelopeResponse'
              example:
                error:
                  status: 403
                  message: La llave no tiene permiso para esta operación.
        '409':
          description: >-
            Conflicto: hay una solicitud en curso con la misma Idempotency-Key,
            o el estado del recurso no permite la operación.
          headers:
            X-Request-Id:
              description: >-
                Identificador de la solicitud (el tuyo si enviaste uno válido).
                Inclúyelo al reportar un problema.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelopeResponse'
              example:
                error:
                  status: 409
                  message: Hay una solicitud en curso con esta Idempotency-Key.
        '422':
          description: La Idempotency-Key ya se usó con una solicitud distinta.
          headers:
            X-Request-Id:
              description: >-
                Identificador de la solicitud (el tuyo si enviaste uno válido).
                Inclúyelo al reportar un problema.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelopeResponse'
              example:
                error:
                  status: 422
                  message: Esta Idempotency-Key ya se usó con otra solicitud.
        '429':
          description: >-
            Límite de solicitudes de la llave (trae `Retry-After`: respétalo) o
            límite mensual de este tipo de e-CF alcanzado (sin `Retry-After`: se
            renueva al inicio del mes siguiente; consulta `GET /v1/limits`).
            Ninguno se guarda como respuesta idempotente.
          headers:
            Retry-After:
              description: >-
                Segundos a esperar antes de reintentar. Solo en el límite de
                solicitudes; no viene en el límite mensual.
              schema:
                type: integer
            X-Request-Id:
              description: >-
                Identificador de la solicitud (el tuyo si enviaste uno válido).
                Inclúyelo al reportar un problema.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelopeResponse'
              examples:
                rateLimit:
                  summary: Límite de solicitudes (con Retry-After)
                  value:
                    error:
                      status: 429
                      message: Demasiadas solicitudes. Intenta de nuevo más tarde.
                monthlyLimit:
                  summary: Límite mensual del tipo de e-CF (sin Retry-After)
                  value:
                    error:
                      status: 429
                      message: >-
                        Alcanzaste el límite mensual de comprobantes 31 (500).
                        Se renueva el 1 de octubre; para ampliarlo contacta a
                        AC-PAY.
        '502':
          description: >-
            Error inesperado del servicio de facturación. Se puede reintentar
            con la misma Idempotency-Key.
          headers:
            X-Request-Id:
              description: >-
                Identificador de la solicitud (el tuyo si enviaste uno válido).
                Inclúyelo al reportar un problema.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelopeResponse'
              example:
                error:
                  status: 502
                  message: >-
                    El servicio de facturación electrónica tuvo un error
                    inesperado. Intenta de nuevo más tarde.
        '503':
          description: >-
            Servicio no disponible temporalmente. Se puede reintentar con la
            misma Idempotency-Key.
          headers:
            X-Request-Id:
              description: >-
                Identificador de la solicitud (el tuyo si enviaste uno válido).
                Inclúyelo al reportar un problema.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelopeResponse'
              example:
                error:
                  status: 503
                  message: >-
                    El servicio de facturación electrónica no está disponible.
                    Intenta de nuevo en unos minutos.
      security:
        - bearer: []
components:
  schemas:
    CreateSpecialDocumentDto:
      type: object
      properties:
        ecfType:
          type: number
          description: >-
            Tipo: 41 compras, 43 gastos menores, 46 exportaciones, 47 pagos al
            exterior.
          enum:
            - 41
            - 43
            - 46
            - 47
          example: 41
        description:
          type: string
          description: Descripción del bien o servicio.
          example: Compra de plátanos a productor informal
          maxLength: 500
        total:
          type: number
          description: Total en RD$, con ITBIS (máximo 2 decimales).
          example: 11800
          minimum: 0
          maximum: 99999999.99
          exclusiveMinimum: true
        itbis:
          type: number
          description: >-
            ITBIS incluido en `total` (máximo 2 decimales, no mayor que
            `total`).
          default: 0
          example: 1800
          minimum: 0
          maximum: 99999999.99
        counterparty:
          description: >-
            Contraparte. Obligatoria en 41 (con `taxId`), 46 y 47 (con
            `foreignId` y `country`); no se envía en 43.
          allOf:
            - $ref: '#/components/schemas/SpecialDocumentCounterpartyDto'
        isrWithheld:
          type: number
          description: ISR retenido en RD$ (máximo 2 decimales). Solo en e-CF 41 y 47.
          example: 1000
          minimum: 0
          maximum: 99999999.99
        itbisWithheld:
          type: number
          description: ITBIS retenido en RD$ (máximo 2 decimales). Solo en e-CF 41.
          example: 1800
          minimum: 0
          maximum: 99999999.99
        goodOrService:
          type: string
          description: Si el comprobante ampara un bien o un servicio.
          enum:
            - good
            - service
      required:
        - ecfType
        - description
        - total
    SpecialDocumentResponse:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: 3f1c2b9e-8a4d-4f6e-9c1a-2b7d5e8f0a13
        status:
          type: string
          enum:
            - pending
            - accepted
            - accepted_with_observations
            - rejected
            - cancelled
            - error
          description: >-
            `pending`: enviado, esperando a la DGII (se actualiza solo en
            segundo plano, y también al consultar). `accepted` /
            `accepted_with_observations`: válido. `rejected`: la DGII lo rechazó
            (ver `statusMessage`). `cancelled`: anulado. `error`: no se pudo
            procesar (se puede reintentar).
          example: accepted
        statusMessage:
          type: string
          nullable: true
          description: Detalle del estado (motivo de rechazo u observaciones).
          example: null
        securityCode:
          type: string
          nullable: true
          description: >-
            Código de seguridad del e-CF (va impreso en la representación
            impresa).
          example: a1B2c3
        stampUrl:
          type: string
          nullable: true
          description: URL de consulta de la DGII (contenido del código QR).
          example: >-
            https://ecf.dgii.gov.do/testecf/ConsultaTimbre?RncEmisor=101000000&RncComprador=131793916&ENCF=E310000000001&FechaEmision=27-09-2026&MontoTotal=3541.18&FechaFirma=27-09-2026%2010:15:00&CodigoSeguridad=a1B2c3
        signedAt:
          type: string
          nullable: true
          format: date-time
          example: '2026-09-27T14:15:00.000Z'
        issuedAt:
          type: string
          format: date-time
          example: '2026-09-27T14:14:58.000Z'
        environment:
          type: string
          enum:
            - test
            - live
          example: test
        currency:
          type: string
          enum:
            - DOP
          example: DOP
        subtotal:
          type: number
          description: Monto sin ITBIS.
          example: 3001
        itbis:
          type: number
          description: ITBIS total.
          example: 540.18
        total:
          type: number
          description: Total con ITBIS.
          example: 3541.18
        encf:
          type: string
          nullable: true
          description: e-NCF asignado. `null` mientras no se haya asignado.
          example: E410000000001
        ecfType:
          type: number
          enum:
            - 41
            - 43
            - 46
            - 47
          description: >-
            41 compras, 43 gastos menores, 46 exportaciones, 47 pagos al
            exterior.
          example: 41
        description:
          type: string
          example: Compra de plátanos a productor informal
        counterparty:
          nullable: true
          description: '`null` en e-CF 43 (gastos menores).'
          type: object
          allOf:
            - $ref: '#/components/schemas/SpecialDocumentCounterpartyResponse'
        isrWithheld:
          type: number
          description: ISR retenido.
          example: 0
        itbisWithheld:
          type: number
          description: ITBIS retenido.
          example: 0
      required:
        - id
        - status
        - statusMessage
        - securityCode
        - stampUrl
        - signedAt
        - issuedAt
        - environment
        - currency
        - subtotal
        - itbis
        - total
        - encf
        - ecfType
        - description
        - counterparty
        - isrWithheld
        - itbisWithheld
    ErrorEnvelopeResponse:
      type: object
      properties:
        error:
          $ref: '#/components/schemas/ErrorEnvelopeBodyResponse'
      required:
        - error
    SpecialDocumentCounterpartyDto:
      type: object
      properties:
        name:
          type: string
          description: Nombre o razón social.
          example: Juan Pérez
          maxLength: 255
        taxId:
          type: string
          description: >-
            RNC (9 dígitos) o cédula (11 dígitos), sin guiones. Obligatorio en
            e-CF 41.
          example: '00112345678'
          pattern: ^(\d{9}|\d{11})$
        foreignId:
          type: string
          description: >-
            Identificación en su país (pasaporte, tax id...). Obligatoria en
            e-CF 46 y 47.
          example: 98-7654321
          maxLength: 50
        country:
          type: string
          description: >-
            País, código ISO 3166-1 alfa-2 en mayúsculas. Obligatorio en e-CF 46
            y 47.
          example: US
          pattern: ^[A-Z]{2}$
      required:
        - name
    SpecialDocumentCounterpartyResponse:
      type: object
      properties:
        name:
          type: string
          nullable: true
          example: Juan Pérez
        taxId:
          type: string
          nullable: true
          description: RNC o cédula (contrapartes locales).
          example: '00112345678'
        foreignId:
          type: string
          nullable: true
          description: Identificación en su país (contrapartes del exterior).
          example: null
        country:
          type: string
          nullable: true
          description: País (ISO 3166-1 alfa-2).
          example: null
      required:
        - name
        - taxId
        - foreignId
        - country
    ErrorEnvelopeBodyResponse:
      type: object
      properties:
        status:
          type: number
          description: Código HTTP (igual al de la respuesta).
          example: 400
        message:
          type: string
          example: La solicitud no es válida.
        details:
          description: 'Detalle por campo, cuando aplica (`ruta.del.campo: mensaje`).'
          example:
            - 'items.0.quantity: debe ser un número entero.'
          type: array
          items:
            type: string
      required:
        - status
        - message
  securitySchemes:
    bearer:
      scheme: bearer
      bearerFormat: acpay_<test|live>_<secreto>
      type: http
      description: Llave de API de AC-PAY del ambiente de esta URL.

````

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