{
  "openapi": "3.1.0",
  "info": {
    "title": "abaPOS e-CF Gateway",
    "version": "1.0.0",
    "summary": "API para casas de software: un OAuth, muchos RNCs, envío a DGII.",
    "description": "API REST de **abaPOS** para casas de software que emiten comprobantes fiscales electrónicos (e-CF)\na nombre de **varios RNCs** con un solo par de credenciales.\n\nabaPOS firma cada documento con el certificado del emisor y lo transmite a la DGII.\nTu sistema solo envía el JSON del comprobante.\n\n## Cómo se organiza\n\n| Concepto | Qué es |\n|---|---|\n| **Tu cuenta** | La casa de software. Un `client_id` y `client_secret`. |\n| **Emisor** | Cada RNC que factura. Tiene su certificado, ambiente DGII y modo de prueba. |\n| **Comprobante** | E31 factura, E32 consumo, E33 débito, E34 crédito, E41 informal, E43 gastos, E44 especiales, E45 gubernamental, E46 exportación, E47 exterior. |\n\nSolicita tu cuenta en https://us.abaposs.com/registro-integracion. El equipo de abaPOS habilita el acceso, entrega las credenciales y da de alta cada emisor.\n\n## Autenticación\n\n```\nPOST /ecf-gateway/oauth/token/\nContent-Type: application/json\n\n{ \"grant_type\": \"client_credentials\", \"client_id\": \"...\", \"client_secret\": \"...\" }\n```\n\nEn el resto de llamadas:\n\n```\nAuthorization: Bearer <access_token>\n```\n\n## Varios emisores\n\nSi solo tienes **un emisor activo**, no hace falta indicarlo.\nSi tienes varios, envía `issuer_id` o `rnc_emisor` en el JSON.\n\n## Ambiente\n\nEl ambiente lo configura abaPOS **por emisor**. No se envía en el request.\n\n- `testecf` — pruebas DGII\n- `CerteCF` — certificación\n- `ecf` — producción\n- Modo sandbox — valida y firma, **no** envía a DGII\n\n## Contingencia\n\nSi la DGII no responde o no hay conexión, abaPOS guarda el comprobante y lo reenvía solo, en varios reintentos.\nNo hace falta repetir el POST. Consulta el estado; si `can_resend` es `true` puedes forzar un reintento.",
    "contact": {
      "name": "abaPOS Integraciones",
      "email": "support@abasuss.com",
      "url": "https://abaposs.com"
    },
    "license": {
      "name": "Uso restringido — Abasus SRL"
    }
  },
  "servers": [
    {
      "url": "http://abasussrl.myabacuss.us/ecf-gateway",
      "description": "Gateway e-CF (este host)"
    }
  ],
  "tags": [
    {
      "name": "Autenticación",
      "description": "OAuth2 client_credentials. Un par de credenciales por partner, válido para todos sus emisores."
    },
    {
      "name": "Facturas E31",
      "description": "Factura de crédito fiscal electrónica (e-CF E31)."
    },
    {
      "name": "Devoluciones E34",
      "description": "Nota de crédito electrónica (e-CF E34)."
    },
    {
      "name": "Compras informales E41",
      "description": "Comprobante de compras a proveedores informales."
    },
    {
      "name": "Certificados",
      "description": "Carga del .p12 / .pfx por emisor (RNC)."
    }
  ],
  "externalDocs": {
    "description": "Documentación interactiva del gateway",
    "url": "http://abasussrl.myabacuss.us/ecf-gateway/docs/"
  },
  "security": [
    {
      "PartnerBearer": []
    }
  ],
  "paths": {
    "/oauth/token/": {
      "post": {
        "tags": [
          "Autenticación"
        ],
        "operationId": "issuePartnerToken",
        "summary": "Obtener token",
        "description": "Emite un access token (30 días) con `client_credentials`. No lleva `Authorization`. El token sirve para **todos** los emisores del partner.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TokenRequest"
              },
              "example": {
                "grant_type": "client_credentials",
                "client_id": "ecf_xxxxxxxx",
                "client_secret": "••••••••"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token emitido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResponse"
                },
                "example": {
                  "access_token": "k7x…",
                  "token_type": "Bearer",
                  "expires_in": 2592000,
                  "refresh_token": "r9p…",
                  "refresh_expires_in": 2592000
                }
              }
            }
          },
          "400": {
            "description": "Credenciales inválidas o partner suspendido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Credenciales inválidas o partner suspendido."
                }
              }
            }
          }
        }
      }
    },
    "/certificate/": {
      "post": {
        "tags": [
          "Certificados"
        ],
        "operationId": "uploadIssuerCertificate",
        "summary": "Cargar certificado .p12",
        "description": "Reemplaza el certificado del emisor. Multipart: `file` + `password`. Si hay varios emisores, indica `issuer_id` o `rnc_emisor`. Luego el equipo de abaPOS activa el emisor para que pueda emitir.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/CertificateUpload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Certificado actualizado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateResult"
                }
              }
            }
          },
          "400": {
            "description": "Archivo, contraseña o emisor inválido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Archivo, contraseña o emisor inválido."
                }
              }
            }
          },
          "401": {
            "description": "Token inválido o expirado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Token inválido o expirado."
                }
              }
            }
          }
        }
      }
    },
    "/invoices/": {
      "get": {
        "tags": [
          "Facturas E31"
        ],
        "operationId": "listInvoices",
        "summary": "Listar facturas",
        "description": "Por defecto solo `document_type=INV`. Usa el query para ver otros tipos.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IssuerId"
          },
          {
            "$ref": "#/components/parameters/Status"
          },
          {
            "$ref": "#/components/parameters/Search"
          },
          {
            "$ref": "#/components/parameters/DateFrom"
          },
          {
            "$ref": "#/components/parameters/DateTo"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/DocumentType"
          }
        ],
        "responses": {
          "200": {
            "description": "Página de comprobantes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentList"
                }
              }
            }
          },
          "401": {
            "description": "Token inválido o expirado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Token inválido o expirado."
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Facturas E31"
        ],
        "operationId": "createInvoice",
        "summary": "Emitir factura E31",
        "description": "Registra la factura, la firma y la envía a DGII (o la deja en modo de prueba si el emisor está en sandbox).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceRequest"
              },
              "examples": {
                "basico": {
                  "$ref": "#/components/examples/InvoiceBody"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Comprobante registrado. Si el emisor no está en sandbox, ya se intentó el envío a DGII.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                },
                "examples": {
                  "aceptado": {
                    "$ref": "#/components/examples/DocumentAccepted"
                  },
                  "sandbox": {
                    "$ref": "#/components/examples/DocumentSandbox"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Falta un campo obligatorio o no se pudo resolver el emisor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Falta un campo obligatorio o no se pudo resolver el emisor."
                }
              }
            }
          },
          "403": {
            "description": "El emisor no está activo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "El emisor no está activo."
                }
              }
            }
          },
          "401": {
            "description": "Token inválido o expirado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Token inválido o expirado."
                }
              }
            }
          }
        }
      }
    },
    "/invoices/{invoice_id}/": {
      "get": {
        "tags": [
          "Facturas E31"
        ],
        "operationId": "getInvoice",
        "summary": "Ver factura",
        "parameters": [
          {
            "$ref": "#/components/parameters/DocumentId"
          }
        ],
        "responses": {
          "200": {
            "description": "Comprobante.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                }
              }
            }
          },
          "404": {
            "description": "Comprobante no encontrado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Comprobante no encontrado."
                }
              }
            }
          },
          "401": {
            "description": "Token inválido o expirado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Token inválido o expirado."
                }
              }
            }
          }
        }
      }
    },
    "/invoices/{invoice_id}/send/": {
      "post": {
        "tags": [
          "Facturas E31"
        ],
        "operationId": "resendInvoice",
        "summary": "Reenviar factura",
        "description": "Solo si `can_resend` es `true` (estados *Intentado*, *Connexion*, *Registrado*).",
        "parameters": [
          {
            "$ref": "#/components/parameters/DocumentId"
          }
        ],
        "responses": {
          "200": {
            "description": "Resultado del reenvío.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                }
              }
            }
          },
          "400": {
            "description": "No se puede reenviar (estado final o emisor inactivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "No se puede reenviar (estado final o emisor inactivo)."
                }
              }
            }
          },
          "404": {
            "description": "Comprobante no encontrado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Comprobante no encontrado."
                }
              }
            }
          },
          "401": {
            "description": "Token inválido o expirado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Token inválido o expirado."
                }
              }
            }
          }
        }
      }
    },
    "/returns/": {
      "get": {
        "tags": [
          "Devoluciones E34"
        ],
        "operationId": "listReturns",
        "summary": "Listar devoluciones",
        "parameters": [
          {
            "$ref": "#/components/parameters/IssuerId"
          },
          {
            "$ref": "#/components/parameters/Status"
          },
          {
            "$ref": "#/components/parameters/Search"
          },
          {
            "$ref": "#/components/parameters/DateFrom"
          },
          {
            "$ref": "#/components/parameters/DateTo"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          }
        ],
        "responses": {
          "200": {
            "description": "Página de comprobantes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentList"
                }
              }
            }
          },
          "401": {
            "description": "Token inválido o expirado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Token inválido o expirado."
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Devoluciones E34"
        ],
        "operationId": "createReturn",
        "summary": "Emitir nota de crédito E34",
        "description": "`fecha_vencimiento_ncf` es obligatorio en el JSON. En notas de crédito no aplica ante la DGII; abaPOS lo gestiona por ti.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReturnRequest"
              },
              "examples": {
                "basico": {
                  "$ref": "#/components/examples/ReturnBody"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Comprobante registrado. Si el emisor no está en sandbox, ya se intentó el envío a DGII.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                },
                "examples": {
                  "aceptado": {
                    "$ref": "#/components/examples/DocumentAccepted"
                  },
                  "sandbox": {
                    "$ref": "#/components/examples/DocumentSandbox"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Falta un campo obligatorio o no se pudo resolver el emisor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Falta un campo obligatorio o no se pudo resolver el emisor."
                }
              }
            }
          },
          "403": {
            "description": "El emisor no está activo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "El emisor no está activo."
                }
              }
            }
          },
          "401": {
            "description": "Token inválido o expirado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Token inválido o expirado."
                }
              }
            }
          }
        }
      }
    },
    "/returns/{invoice_id}/": {
      "get": {
        "tags": [
          "Devoluciones E34"
        ],
        "operationId": "getReturn",
        "summary": "Ver devolución",
        "parameters": [
          {
            "$ref": "#/components/parameters/DocumentId"
          }
        ],
        "responses": {
          "200": {
            "description": "Comprobante.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                }
              }
            }
          },
          "404": {
            "description": "Comprobante no encontrado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Comprobante no encontrado."
                }
              }
            }
          },
          "401": {
            "description": "Token inválido o expirado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Token inválido o expirado."
                }
              }
            }
          }
        }
      }
    },
    "/returns/{invoice_id}/send/": {
      "post": {
        "tags": [
          "Devoluciones E34"
        ],
        "operationId": "resendReturn",
        "summary": "Reenviar devolución",
        "parameters": [
          {
            "$ref": "#/components/parameters/DocumentId"
          }
        ],
        "responses": {
          "200": {
            "description": "Resultado del reenvío.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                }
              }
            }
          },
          "400": {
            "description": "No se puede reenviar (estado final o emisor inactivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "No se puede reenviar (estado final o emisor inactivo)."
                }
              }
            }
          },
          "404": {
            "description": "Comprobante no encontrado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Comprobante no encontrado."
                }
              }
            }
          },
          "401": {
            "description": "Token inválido o expirado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Token inválido o expirado."
                }
              }
            }
          }
        }
      }
    },
    "/informal/": {
      "get": {
        "tags": [
          "Compras informales E41"
        ],
        "operationId": "listInformal",
        "summary": "Listar compras informales",
        "parameters": [
          {
            "$ref": "#/components/parameters/IssuerId"
          },
          {
            "$ref": "#/components/parameters/Status"
          },
          {
            "$ref": "#/components/parameters/Search"
          },
          {
            "$ref": "#/components/parameters/DateFrom"
          },
          {
            "$ref": "#/components/parameters/DateTo"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          }
        ],
        "responses": {
          "200": {
            "description": "Página de comprobantes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentList"
                }
              }
            }
          },
          "401": {
            "description": "Token inválido o expirado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Token inválido o expirado."
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Compras informales E41"
        ],
        "operationId": "createInformal",
        "summary": "Emitir compra informal E41",
        "description": "El emisor es quien compra. El proveedor informal va en `nombre_proveedor` y `cedula_proveedor`. En las líneas puedes usar `cantidad` / `precio_unitario` o `qty` / `price`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InformalRequest"
              },
              "examples": {
                "basico": {
                  "$ref": "#/components/examples/InformalBody"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Comprobante registrado. Si el emisor no está en sandbox, ya se intentó el envío a DGII.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                },
                "examples": {
                  "aceptado": {
                    "$ref": "#/components/examples/DocumentAccepted"
                  },
                  "sandbox": {
                    "$ref": "#/components/examples/DocumentSandbox"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Falta un campo obligatorio o no se pudo resolver el emisor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Falta un campo obligatorio o no se pudo resolver el emisor."
                }
              }
            }
          },
          "403": {
            "description": "El emisor no está activo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "El emisor no está activo."
                }
              }
            }
          },
          "401": {
            "description": "Token inválido o expirado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Token inválido o expirado."
                }
              }
            }
          }
        }
      }
    },
    "/informal/{invoice_id}/": {
      "get": {
        "tags": [
          "Compras informales E41"
        ],
        "operationId": "getInformal",
        "summary": "Ver compra informal",
        "parameters": [
          {
            "$ref": "#/components/parameters/DocumentId"
          }
        ],
        "responses": {
          "200": {
            "description": "Comprobante.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                }
              }
            }
          },
          "404": {
            "description": "Comprobante no encontrado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Comprobante no encontrado."
                }
              }
            }
          },
          "401": {
            "description": "Token inválido o expirado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Token inválido o expirado."
                }
              }
            }
          }
        }
      }
    },
    "/informal/{invoice_id}/send/": {
      "post": {
        "tags": [
          "Compras informales E41"
        ],
        "operationId": "resendInformal",
        "summary": "Reenviar compra informal",
        "parameters": [
          {
            "$ref": "#/components/parameters/DocumentId"
          }
        ],
        "responses": {
          "200": {
            "description": "Resultado del reenvío.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                }
              }
            }
          },
          "400": {
            "description": "No se puede reenviar (estado final o emisor inactivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "No se puede reenviar (estado final o emisor inactivo)."
                }
              }
            }
          },
          "404": {
            "description": "Comprobante no encontrado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Comprobante no encontrado."
                }
              }
            }
          },
          "401": {
            "description": "Token inválido o expirado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Token inválido o expirado."
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "PartnerBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "opaque",
        "description": "Token emitido por `POST /oauth/token/`. Envíalo en `Authorization: Bearer <access_token>`. Vigencia: 30 días."
      }
    },
    "schemas": {
      "DocumentType": {
        "type": "string",
        "enum": [
          "INV",
          "DEV",
          "PI"
        ],
        "description": "INV = E31 factura · DEV = E34 devolución · PI = E41 informal."
      },
      "InvoiceStatus": {
        "type": "string",
        "enum": [
          "Registrado",
          "Intentado",
          "Connexion",
          "Sandbox",
          "Aceptado",
          "Aceptado Condicional",
          "Rechazado"
        ]
      },
      "DgiiEnvironment": {
        "type": "string",
        "enum": [
          "testecf",
          "CerteCF",
          "ecf"
        ],
        "description": "Ambiente DGII del **emisor**, no del request."
      },
      "TokenRequest": {
        "type": "object",
        "required": [
          "client_id",
          "client_secret"
        ],
        "properties": {
          "grant_type": {
            "type": "string",
            "enum": [
              "client_credentials"
            ],
            "default": "client_credentials"
          },
          "client_id": {
            "type": "string"
          },
          "client_secret": {
            "type": "string",
            "format": "password"
          }
        }
      },
      "TokenResponse": {
        "type": "object",
        "required": [
          "access_token",
          "token_type",
          "expires_in"
        ],
        "properties": {
          "access_token": {
            "type": "string"
          },
          "token_type": {
            "type": "string",
            "example": "Bearer"
          },
          "expires_in": {
            "type": "integer",
            "example": 2592000,
            "description": "Segundos (30 días)."
          },
          "refresh_token": {
            "type": "string"
          },
          "refresh_expires_in": {
            "type": "integer",
            "example": 2592000
          }
        }
      },
      "CertificateUpload": {
        "type": "object",
        "required": [
          "file",
          "password"
        ],
        "properties": {
          "file": {
            "type": "string",
            "format": "binary",
            "description": "Archivo `.p12` o `.pfx`."
          },
          "password": {
            "type": "string",
            "format": "password"
          },
          "issuer_id": {
            "type": "integer"
          },
          "rnc_emisor": {
            "type": "string"
          },
          "rncAssigned": {
            "type": "string",
            "description": "RNC impreso en el certificado, si aplica."
          }
        }
      },
      "CertificateResult": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "example": "Certificado actualizado"
          },
          "certificate": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "LineItem": {
        "type": "object",
        "required": [
          "cantidad",
          "precio_unitario",
          "subtotal",
          "descripcion"
        ],
        "properties": {
          "cantidad": {
            "type": "number",
            "example": 1
          },
          "impuesto": {
            "type": "number",
            "example": 34.32,
            "description": "ITBIS de la línea."
          },
          "precio_unitario": {
            "type": "number",
            "example": 190.68
          },
          "unidad_medida": {
            "type": "string",
            "example": "UD"
          },
          "codigo_producto": {
            "type": "string",
            "example": "TAZ03"
          },
          "tipo_impuesto": {
            "type": "integer",
            "example": 1,
            "description": "1 = ITBIS 18%, 2 = ITBIS 16%, 4 = exento."
          },
          "subtotal": {
            "type": "number",
            "example": 190.68
          },
          "descripcion": {
            "type": "string",
            "example": "TAZA MAGICA SUBLIMACION 11 OZ"
          },
          "tipo_producto": {
            "type": "integer",
            "example": 1,
            "description": "1 = ITBIS 18%, 2 = ITBIS 16%, 4 = exento."
          }
        }
      },
      "InvoiceRequest": {
        "type": "object",
        "required": [
          "numero_factura",
          "ncf",
          "fecha_factura",
          "total",
          "subtotal",
          "itbis",
          "nombre_cliente",
          "rnc_cliente",
          "lista_productos"
        ],
        "properties": {
          "issuer_id": {
            "type": "integer",
            "description": "Obligatorio si el partner tiene más de un emisor activo."
          },
          "rnc_emisor": {
            "type": "string",
            "example": "130000000",
            "description": "Alternativa a `issuer_id`. Alias: `rnc_emisor_factura`."
          },
          "numero_factura": {
            "type": "string",
            "example": "0000000001"
          },
          "ncf": {
            "type": "string",
            "example": "E310000010003",
            "description": "e-NCF E31 de 13 caracteres."
          },
          "fecha_factura": {
            "type": "string",
            "format": "date",
            "example": "2025-07-28"
          },
          "itbis": {
            "type": "number",
            "example": 34.32
          },
          "propina": {
            "type": "number",
            "example": 0
          },
          "terminos": {
            "type": "string",
            "example": "De contado"
          },
          "total": {
            "type": "number",
            "example": 225
          },
          "descuento_total": {
            "type": "number",
            "example": 0
          },
          "subtotal": {
            "type": "number",
            "example": 190.68
          },
          "fecha_vencimiento_ncf": {
            "type": "string",
            "format": "date",
            "example": "2028-12-31"
          },
          "nombre_cliente": {
            "type": "string",
            "example": "CLIENTE EJEMPLO SRL"
          },
          "rnc_cliente": {
            "type": "string",
            "example": "101000000"
          },
          "lista_productos": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LineItem"
            }
          }
        }
      },
      "ReturnRequest": {
        "type": "object",
        "required": [
          "numero_factura",
          "ncf_factura",
          "ncf_nota_credito",
          "fecha_factura",
          "fecha_nota_credito",
          "fecha_vencimiento_ncf",
          "total",
          "subtotal",
          "itbis",
          "nombre_cliente",
          "rnc_cliente",
          "lista_productos"
        ],
        "properties": {
          "issuer_id": {
            "type": "integer",
            "description": "Obligatorio si el partner tiene más de un emisor activo."
          },
          "rnc_emisor": {
            "type": "string",
            "example": "130000000",
            "description": "Alternativa a `issuer_id`. Alias: `rnc_emisor_factura`."
          },
          "numero_factura": {
            "type": "string",
            "example": "0000000001"
          },
          "ncf_factura": {
            "type": "string",
            "example": "E310000010003",
            "description": "e-NCF de la factura que se anula."
          },
          "ncf_nota_credito": {
            "type": "string",
            "example": "E340001000001"
          },
          "fecha_factura": {
            "type": "string",
            "format": "date",
            "example": "2025-07-28"
          },
          "fecha_nota_credito": {
            "type": "string",
            "format": "date",
            "example": "2025-07-28"
          },
          "fecha_vencimiento_ncf": {
            "type": "string",
            "format": "date",
            "example": "2028-12-31",
            "description": "Requerido en el JSON. En E34 no aplica ante DGII."
          },
          "itbis": {
            "type": "number",
            "example": 34.32
          },
          "propina": {
            "type": "number",
            "example": 0
          },
          "terminos": {
            "type": "string",
            "example": "De contado"
          },
          "total": {
            "type": "number",
            "example": 225
          },
          "descuento_total": {
            "type": "number",
            "example": 0
          },
          "subtotal": {
            "type": "number",
            "example": 190.68
          },
          "nombre_cliente": {
            "type": "string",
            "example": "CLIENTE EJEMPLO SRL"
          },
          "rnc_cliente": {
            "type": "string",
            "example": "101000000"
          },
          "lista_productos": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LineItem"
            }
          }
        }
      },
      "InformalRequest": {
        "type": "object",
        "required": [
          "fecha_factura",
          "ncf",
          "total",
          "subtotal",
          "lista_productos",
          "fecha_vencimiento",
          "cedula_proveedor"
        ],
        "properties": {
          "issuer_id": {
            "type": "integer",
            "description": "Obligatorio si el partner tiene más de un emisor activo."
          },
          "rnc_emisor": {
            "type": "string",
            "example": "130000000",
            "description": "Alternativa a `issuer_id`. Alias: `rnc_emisor_factura`."
          },
          "ncf": {
            "type": "string",
            "example": "E410000000001"
          },
          "fecha_factura": {
            "type": "string",
            "format": "date",
            "example": "2025-07-28"
          },
          "fecha_vencimiento": {
            "type": "string",
            "format": "date",
            "example": "2028-12-31"
          },
          "total": {
            "type": "number",
            "example": 5000
          },
          "subtotal": {
            "type": "number",
            "example": 5000
          },
          "itbis": {
            "type": "number",
            "example": 0
          },
          "cedula_proveedor": {
            "type": "string",
            "example": "00112345678"
          },
          "nombre_proveedor": {
            "type": "string",
            "example": "JUAN PEREZ"
          },
          "nomnbre_proveedor": {
            "type": "string",
            "description": "Alias aceptado de `nombre_proveedor`."
          },
          "itbis_retenido": {
            "type": "number",
            "example": 0
          },
          "isr_retenido": {
            "type": "number",
            "example": 0
          },
          "TotalISRRetencion": {
            "type": "number",
            "description": "Alias de `isr_retenido`."
          },
          "lista_productos": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LineItem"
            }
          }
        }
      },
      "Document": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 24
          },
          "ncf": {
            "type": "string",
            "example": "E310000010003"
          },
          "document_type": {
            "$ref": "#/components/schemas/DocumentType"
          },
          "ncf_factura": {
            "type": "string",
            "description": "Solo en devoluciones."
          },
          "remote_order_id": {
            "type": "string",
            "example": "0000000001"
          },
          "invoice_status": {
            "$ref": "#/components/schemas/InvoiceStatus"
          },
          "dgii_response": {
            "type": "string"
          },
          "xml_code": {
            "type": "string",
            "description": "Código de seguridad de 6 caracteres."
          },
          "timbre_url": {
            "type": "string",
            "format": "uri"
          },
          "partner_id": {
            "type": "integer"
          },
          "issuer_id": {
            "type": "integer"
          },
          "issuer_rnc": {
            "type": "string"
          },
          "issuer_name": {
            "type": "string"
          },
          "environment": {
            "$ref": "#/components/schemas/DgiiEnvironment"
          },
          "sandbox": {
            "type": "boolean"
          },
          "track_id": {
            "type": "string"
          },
          "mensajes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DgiiMessage"
            }
          },
          "can_resend": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "date_joined": {
            "type": "string",
            "format": "date"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "DocumentList": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Document"
            }
          },
          "count": {
            "type": "integer"
          },
          "page": {
            "type": "integer"
          },
          "page_size": {
            "type": "integer"
          },
          "total_pages": {
            "type": "integer"
          },
          "date_from": {
            "type": "string"
          },
          "date_to": {
            "type": "string"
          }
        }
      },
      "DgiiMessage": {
        "type": "object",
        "properties": {
          "codigo": {
            "type": "integer",
            "example": 3
          },
          "valor": {
            "type": "string"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      }
    },
    "examples": {
      "InvoiceBody": {
        "summary": "Factura E31",
        "value": {
          "numero_factura": "0000000001",
          "ncf": "E310000010003",
          "fecha_factura": "2025-07-28",
          "itbis": 34.32,
          "propina": 0,
          "terminos": "De contado",
          "total": 225,
          "descuento_total": 0,
          "subtotal": 190.68,
          "fecha_vencimiento_ncf": "2028-12-31",
          "nombre_cliente": "CLIENTE EJEMPLO SRL",
          "rnc_cliente": "101000000",
          "rnc_emisor": "130000000",
          "lista_productos": [
            {
              "cantidad": 1,
              "impuesto": 34.32,
              "precio_unitario": 190.68,
              "unidad_medida": "UD",
              "codigo_producto": "TAZ03",
              "tipo_impuesto": 1,
              "subtotal": 190.68,
              "descripcion": "TAZA MAGICA SUBLIMACION 11 OZ",
              "tipo_producto": 1
            }
          ]
        }
      },
      "ReturnBody": {
        "summary": "Devolución E34",
        "value": {
          "numero_factura": "0000000001",
          "fecha_factura": "2025-07-28",
          "itbis": 34.32,
          "propina": 0,
          "terminos": "De contado",
          "total": 225,
          "descuento_total": 0,
          "subtotal": 190.68,
          "fecha_vencimiento_ncf": "2028-12-31",
          "nombre_cliente": "CLIENTE EJEMPLO SRL",
          "rnc_cliente": "101000000",
          "rnc_emisor": "130000000",
          "lista_productos": [
            {
              "cantidad": 1,
              "impuesto": 34.32,
              "precio_unitario": 190.68,
              "unidad_medida": "UD",
              "codigo_producto": "TAZ03",
              "tipo_impuesto": 1,
              "subtotal": 190.68,
              "descripcion": "TAZA MAGICA SUBLIMACION 11 OZ",
              "tipo_producto": 1
            }
          ],
          "ncf_factura": "E310000010003",
          "ncf_nota_credito": "E340001000001",
          "fecha_nota_credito": "2025-07-28"
        }
      },
      "InformalBody": {
        "summary": "Compra informal E41",
        "value": {
          "ncf": "E410000000001",
          "fecha_factura": "2025-07-28",
          "fecha_vencimiento": "2028-12-31",
          "total": 5000,
          "subtotal": 5000,
          "itbis": 0,
          "cedula_proveedor": "00112345678",
          "nombre_proveedor": "JUAN PEREZ",
          "rnc_emisor": "130000000",
          "lista_productos": [
            {
              "cantidad": 1,
              "impuesto": 0,
              "precio_unitario": 5000,
              "unidad_medida": "UD",
              "codigo_producto": "SRV01",
              "tipo_impuesto": 4,
              "subtotal": 5000,
              "descripcion": "SERVICIO DE PLOMERIA",
              "tipo_producto": 4
            }
          ]
        }
      },
      "DocumentAccepted": {
        "summary": "Aceptado por DGII",
        "value": {
          "id": 24,
          "ncf": "E310000010003",
          "document_type": "INV",
          "ncf_factura": "",
          "remote_order_id": "0000000001",
          "invoice_status": "Aceptado",
          "dgii_response": "Aceptado",
          "xml_code": "AbC12x",
          "timbre_url": "https://ecf.dgii.gov.do/testecf/ConsultaTimbre?RNCEmisor=130000000&eNCF=E310000010003",
          "partner_id": 1,
          "issuer_id": 2,
          "issuer_rnc": "130000000",
          "issuer_name": "EMPRESA EJEMPLO SRL",
          "environment": "testecf",
          "sandbox": false,
          "track_id": "00000000-0000-4000-8000-000000000001",
          "mensajes": [],
          "can_resend": false,
          "created_at": "2026-09-03T22:39:35.203842+00:00",
          "date_joined": "2025-07-28",
          "message": "Comprobante enviado a DGII."
        }
      },
      "DocumentSandbox": {
        "summary": "Sandbox (no enviado)",
        "value": {
          "id": 24,
          "ncf": "E310000010003",
          "document_type": "INV",
          "ncf_factura": "",
          "remote_order_id": "0000000001",
          "invoice_status": "Sandbox",
          "dgii_response": "Sandbox",
          "xml_code": "AbC12x",
          "timbre_url": "https://ecf.dgii.gov.do/testecf/ConsultaTimbre?RNCEmisor=130000000&eNCF=E310000010003",
          "partner_id": 1,
          "issuer_id": 2,
          "issuer_rnc": "130000000",
          "issuer_name": "EMPRESA EJEMPLO SRL",
          "environment": "testecf",
          "sandbox": false,
          "track_id": "",
          "mensajes": [],
          "can_resend": false,
          "created_at": "2026-09-03T22:39:35.203842+00:00",
          "date_joined": "2025-07-28",
          "message": "Modo sandbox: el comprobante se validó y no se envió a DGII."
        }
      }
    },
    "parameters": {
      "DocumentId": {
        "name": "invoice_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer"
        },
        "description": "ID interno del comprobante en el gateway."
      },
      "IssuerId": {
        "name": "issuer_id",
        "in": "query",
        "schema": {
          "type": "integer"
        },
        "description": "Filtra por emisor."
      },
      "Status": {
        "name": "status",
        "in": "query",
        "schema": {
          "$ref": "#/components/schemas/InvoiceStatus"
        },
        "description": "`Aceptado` incluye también *Aceptado Condicional*."
      },
      "Search": {
        "name": "search",
        "in": "query",
        "schema": {
          "type": "string"
        },
        "description": "NCF, número de factura, código de seguridad, RNC o razón social del emisor."
      },
      "DateFrom": {
        "name": "date_from",
        "in": "query",
        "schema": {
          "type": "string",
          "format": "date"
        },
        "description": "Desde (creación). Acepta `YYYY-MM-DD`, `DD-MM-YYYY` o `DD/MM/YYYY`. Alias: `from`."
      },
      "DateTo": {
        "name": "date_to",
        "in": "query",
        "schema": {
          "type": "string",
          "format": "date"
        },
        "description": "Hasta (creación). Alias: `to`."
      },
      "Page": {
        "name": "page",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 1
        }
      },
      "PageSize": {
        "name": "page_size",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 20
        }
      },
      "DocumentType": {
        "name": "document_type",
        "in": "query",
        "schema": {
          "$ref": "#/components/schemas/DocumentType"
        },
        "description": "Solo en `GET /invoices/`. Por defecto `INV`."
      }
    }
  },
  "x-tagGroups": [
    {
      "name": "Inicio",
      "tags": [
        "Autenticación",
        "Certificados"
      ]
    },
    {
      "name": "Comprobantes",
      "tags": [
        "Facturas E31",
        "Devoluciones E34",
        "Compras informales E41"
      ]
    }
  ]
}