{
  "openapi": "3.1.0",
  "info": {
    "title": "ARCA Extractor API",
    "description": "API asincrónica de extracción para servicios de ARCA (Agencia de Recaudación y Control Aduanero). Implementa el patrón asincrónico Request-Reply con cifrado efímero de credenciales fiscales (RSA-OAEP-SHA256) sin custodia de contraseñas.",
    "version": "0.3.9",
    "contact": {
      "name": "ARCA Extractor Team"
    }
  },
  "servers": [
    {
      "url": "https://api.arcanoe.com.ar",
      "description": "Servidor VPS público (Cloudflare Tunnel)"
    },
    {
      "url": "http://localhost:3000",
      "description": "Servidor local de desarrollo"
    },
    {
      "url": "/",
      "description": "Servidor local / relativo"
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "summary": "Verificación de estado del servicio",
        "description": "Endpoint público sin autenticación para probes de liveness y readiness.",
        "operationId": "checkHealth",
        "security": [],
        "responses": {
          "200": {
            "description": "Servicio saludable",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "version": {
                      "type": "string",
                      "example": "0.1.0"
                    }
                  },
                  "required": [
                    "ok",
                    "version"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/credenciales/public-key": {
      "get": {
        "summary": "Obtener clave pública RSA efímera",
        "description": "Devuelve la clave pública RSA (2048 bits, PKCS#8/SPKI PEM) para cifrar la clave fiscal mediante RSA-OAEP-SHA256 en el cliente antes de invocar POST /api/v2/jobs.",
        "operationId": "getPublicKey",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Clave pública RSA obtenida exitosamente",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "example": "max-age=300, public"
                },
                "description": "Directiva de caché HTTP"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicKeyResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v2/jobs": {
      "post": {
        "summary": "Crear un nuevo job de extracción asincrónica",
        "description": "Encola una solicitud de extracción en ARCA para los servicios autorizados (Mis Comprobantes o Liquidaciones de Sector Pecuario). Valida formato CUIT, ventana de fechas (máximo 365 días), descifra la clave en memoria efímera sin persistencia y retorna inmediatamente un 202 con el ID del job. El header opcional `Idempotency-Key` agrega la misma semántica de idempotencia que la emisión WSFE: misma clave con el mismo payload devuelve el job original (200 con `replay: true`), misma clave con payload distinto responde 409 `idempotency_conflict_payload`, y una clave con un intento en vuelo responde 409 `idempotency_in_flight`.",
        "operationId": "createJob",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Clave de idempotencia de la creación. Opcional: sin el header el comportamiento es el de siempre (un job nuevo por request). Con el header, la clave se particiona por ambiente y por tenant, y vive 7 días (la misma ventana que la emisión). Presente pero vacía responde 400 `idempotency_key_requerida`.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "example": "acme-orden-9842"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CrearJobRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Replay idempotente: la misma `Idempotency-Key` con el mismo payload ya había creado este job. Devuelve el `job_id` original con su estado ACTUAL (el estado autoritativo se lee en GET /api/v2/jobs/{job_id}) y `replay: true`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CrearJobResponse"
                }
              }
            }
          },
          "202": {
            "description": "Job aceptado y encolado para ejecución",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CrearJobResponse"
                }
              }
            }
          },
          "400": {
            "description": "Solicitud rechazada por parámetros inválidos, credenciales inválidas o servicio no soportado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenAmbiente"
          },
          "409": {
            "description": "Conflicto de idempotencia: la clave ya existe con otro payload (`idempotency_conflict_payload`) o tiene una creación en vuelo (`idempotency_in_flight`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "La clave de idempotencia fue rechazada y no se puede reutilizar (`intento_rechazado`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobErrorResponse"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "get": {
        "summary": "Listar los jobs del tenant",
        "description": "Devuelve los jobs que pertenecen al tenant de la credencial (el `tenantId` sale exclusivamente de la API key: no hay parámetro que lo elija), ordenados del más nuevo al más viejo y paginados. Los filtros son opcionales. Un job de otro tenant no aparece nunca, ni en `jobs` ni en `total`.",
        "operationId": "listJobs",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "estado",
            "in": "query",
            "required": false,
            "description": "Filtra por estado consolidado del ciclo de vida del job.",
            "schema": {
              "$ref": "#/components/schemas/JobEstado"
            }
          },
          {
            "name": "tipo",
            "in": "query",
            "required": false,
            "description": "Filtra por tipo consultado (el mismo enum que `opciones.tipo` en la creación).",
            "schema": {
              "type": "string",
              "enum": [
                "mc_recibidos",
                "mc_emitidos",
                "lsp_liquidaciones",
                "rcel",
                "srt_expedientes",
                "srt_expedientes_historico",
                "srt_comunicaciones"
              ]
            }
          },
          {
            "name": "desde",
            "in": "query",
            "required": false,
            "description": "Fecha de creación mínima (inclusive) en formato DD/MM/AAAA.",
            "schema": {
              "type": "string",
              "pattern": "^\\d{2}/\\d{2}/\\d{4}$",
              "example": "01/08/2026"
            }
          },
          {
            "name": "hasta",
            "in": "query",
            "required": false,
            "description": "Fecha de creación máxima (inclusive) en formato DD/MM/AAAA.",
            "schema": {
              "type": "string",
              "pattern": "^\\d{2}/\\d{2}/\\d{4}$",
              "example": "14/09/2026"
            }
          },
          {
            "name": "limite",
            "in": "query",
            "required": false,
            "description": "Tamaño de página (1..100). Por defecto 20; fuera de rango responde 400.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "desplazamiento",
            "in": "query",
            "required": false,
            "description": "Cantidad de jobs a saltear (paginación por offset). Por defecto 0.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Página de jobs del tenant",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListaJobsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Filtro inválido: estado o fecha malformados, paginación fuera de rango (`parametros_invalidos`) o tipo no registrado (`servicio_no_soportado`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenAmbiente"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v2/jobs/{job_id}": {
      "get": {
        "summary": "Consultar estado y resultados de un job",
        "description": "Polling de ciclo de vida del job. Retorna el estado global (encolado -> ejecutando -> completado | parcial | requiere_humano | fallido), conteo auditado (declarado vs obtenido) y las filas normalizadas en caso de completitud exitosa. Un job puede llevar varios servicios: `servicios[]` trae un elemento por servicio pedido, en el orden del pedido, cada uno con su `estado`, su `motivo` tipado, su `conteo` y sus `filas`; con resultados mixtos el job agrega a `parcial`.",
        "operationId": "getJob",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "job_id",
            "in": "path",
            "required": true,
            "description": "Identificador único del job devuelto en POST /api/v2/jobs",
            "schema": {
              "type": "string",
              "example": "job_a1b2c3d4e5f60718"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Estado del job y comprobantes si completó",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobResponse"
                }
              }
            }
          },
          "400": {
            "description": "Identificador de job malformado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Job no encontrado o expirado por TTL de retención",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "no_encontrado"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v2/jobs/{job_id}/retry": {
      "post": {
        "summary": "Reintentar un job",
        "description": "Encola un job NUEVO con el payload operativo del job original: TODOS sus servicios, cada uno con su propia ventana y en el orden pedido, más CUITs y envelope. El original no se toca: queda como evidencia de lo que ya pasó. El job nuevo queda enlazado con `reintento_de` (visible tanto en esta respuesta como en GET /api/v2/jobs/{job_id}). Un job multi-servicio se reintenta entero: el retry reabre cada servicio sobre su ventana original, y con resultados mixtos el job nuevo agrega a `parcial` igual que el original. La clave fiscal NO se persiste nunca, así que el consumidor la vuelve a mandar cifrada en el cuerpo: no hay forma de reintentar sin volver a presentar la credencial.",
        "operationId": "retryJob",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "job_id",
            "in": "path",
            "required": true,
            "description": "Identificador del job original a reintentar.",
            "schema": {
              "type": "string",
              "example": "job_a1b2c3d4e5f60718"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReintentarJobRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job nuevo encolado; `reintento_de` apunta al original",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CrearJobResponse"
                }
              }
            }
          },
          "400": {
            "description": "Cuerpo malformado o sin `clave_encriptada` (`parametros_invalidos`) o clave fiscal indescifrable (`credencial_invalida`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenAmbiente"
          },
          "404": {
            "description": "El job no existe o pertenece a otro tenant. Los dos casos son indistinguibles a propósito: un tenant no puede sondear la existencia de jobs ajenos.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "no_encontrado"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v2/ws/wsfe/sales-points": {
      "get": {
        "summary": "Consultar puntos de venta habilitados (WSFE)",
        "description": "Consulta de solo lectura sobre WSFE vía @arcasdk/core (siempre homologación). Retorna los puntos de venta habilitados con campos whitelisteados; nunca emite comprobantes ni lee credenciales.",
        "operationId": "getSalesPoints",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Puntos de venta habilitados según AFIP (lista vacía válida con campo `aviso` cuando homologación no informa ninguno)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PuntosVentaResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSFE/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Consulta no configurada en este servidor (bootstrap sin conexión WS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wsfe/last-voucher": {
      "get": {
        "summary": "Consultar último comprobante autorizado (WSFE)",
        "description": "Consulta de solo lectura sobre WSFE vía @arcasdk/core (siempre homologación). Retorna el último número de comprobante autorizado para el punto de venta y tipo dados (0 cuando nunca se emitió ninguno: éxito válido, no error); es la primitiva de reconciliación previa a cualquier emisión. Nunca emite comprobantes ni lee credenciales.",
        "operationId": "getLastVoucher",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "punto_venta",
            "in": "query",
            "required": true,
            "description": "Punto de venta AFIP (entero positivo).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "example": 2
            }
          },
          {
            "name": "tipo_comprobante",
            "in": "query",
            "required": true,
            "description": "Tipo de comprobante AFIP (entero positivo).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "example": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Último comprobante autorizado según AFIP (0 cuando el punto de venta está limpio)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UltimoComprobanteResponse"
                }
              }
            }
          },
          "400": {
            "description": "punto_venta o tipo_comprobante inválido o ausente (no entero positivo); el proveedor nunca fue invocado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSFE/AFIP no disponible o con error (detalle sanitizado)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Consulta no configurada en este servidor (bootstrap sin conexión WS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wsfe/vouchers": {
      "post": {
        "summary": "Emitir comprobante electrónico (WSFE)",
        "description": "Emisión idempotente de comprobante electrónico (Factura A/B/C) con pre-flight reconciliation. Requiere header Idempotency-Key. Devuelve 201 en primera emisión y 200 con replay: true en reintentos con misma clave y payload.",
        "operationId": "createVoucher",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Clave única de idempotencia para la transacción.",
            "schema": {
              "type": "string",
              "example": "orden-12345"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmitirVoucherRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Replay de comprobante previamente autorizado sin invocar a ARCA.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmitirVoucherResponse"
                }
              }
            }
          },
          "201": {
            "description": "Comprobante emitido exitosamente por primera vez.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmitirVoucherResponse"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos o header Idempotency-Key ausente.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "409": {
            "description": "Conflicto de idempotencia (clave en curso o reutilizada con distinto payload).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Rechazo formal de AFIP o clave previamente rechazada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor no disponible o estado incierto registrado ante timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Servicio no configurado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wsfe/vouchers/{numero}": {
      "get": {
        "summary": "Consultar comprobante por número (WSFE)",
        "description": "Consulta de solo lectura sobre WSFE vía @arcasdk/core (siempre homologación). Retorna el detalle del comprobante con campos whitelisteados. Nunca emite comprobantes ni lee credenciales.",
        "operationId": "getVoucherInfo",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "numero",
            "in": "path",
            "required": true,
            "description": "Número de comprobante (entero positivo).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "example": 7
            }
          },
          {
            "name": "punto_venta",
            "in": "query",
            "required": true,
            "description": "Punto de venta AFIP (entero positivo).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "example": 2
            }
          },
          {
            "name": "tipo_comprobante",
            "in": "query",
            "required": true,
            "description": "Tipo de comprobante AFIP (entero positivo).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "example": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Comprobante encontrado según AFIP",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ComprobanteInfoResponse"
                }
              }
            }
          },
          "400": {
            "description": "número, punto_venta o tipo_comprobante inválido o ausente (no entero positivo); el proveedor nunca fue invocado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Comprobante no encontrado (ausencia limpia informada por el proveedor)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSFE/AFIP no disponible o con error, o DTO definido-pero-inutilizable (detalle sanitizado; nunca falso 404)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Consulta no configurada en este servidor (bootstrap sin conexión WS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/padron/a4/{cuit}": {
      "get": {
        "summary": "Consultar contribuyente por CUIT (padrón A4)",
        "description": "Consulta de solo lectura sobre el padrón alcance 4 vía @arcasdk/core (siempre homologación). Valida el CUIT sintácticamente antes de invocar al proveedor; retorna el contribuyente con campos whitelisteados. Nunca emite comprobantes ni lee credenciales.",
        "operationId": "getPadronA4",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "cuit",
            "in": "path",
            "required": true,
            "description": "CUIT del contribuyente (11 dígitos con dígito verificador válido).",
            "schema": {
              "type": "string",
              "example": "20123456786"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Contribuyente encontrado según AFIP",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PadronA4Response"
                }
              }
            }
          },
          "400": {
            "description": "CUIT inválido (formato o dígito verificador); el proveedor nunca fue invocado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Contribuyente no encontrado en el padrón (ausencia limpia informada por el proveedor)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor padrón/AFIP no disponible o con error (detalle sanitizado)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Consulta no configurada en este servidor (bootstrap sin conexión WS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wslsp/provincias": {
      "get": {
        "summary": "Consultar provincias (WSLSP)",
        "description": "Consulta de solo lectura sobre WSLSP vía @arcasdk/core (siempre homologación). Retorna las provincias con campos whitelisteados; nunca genera liquidaciones ni lee credenciales.",
        "operationId": "getWslspProvincias",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Provincias según AFIP (lista vacía válida con campo `aviso` cuando homologación no informa filas)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WslspProvinciasResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSLSP/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Consulta no configurada en este servidor (bootstrap sin conexión WS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wslsp/puntos-venta": {
      "get": {
        "summary": "Consultar puntos de venta (WSLSP)",
        "description": "Consulta de solo lectura sobre WSLSP vía @arcasdk/core (siempre homologación). Retorna los puntos de venta con campos whitelisteados; nunca genera liquidaciones ni lee credenciales.",
        "operationId": "getWslspPuntosVenta",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Puntos de venta según AFIP (lista vacía válida con campo `aviso`: homologación no valida la existencia de puntos de venta)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WslspPuntosVentaResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSLSP/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Consulta no configurada en este servidor (bootstrap sin conexión WS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wslsp/tipos-comprobante": {
      "get": {
        "summary": "Consultar tipos de comprobante (WSLSP)",
        "description": "Consulta de solo lectura sobre WSLSP vía @arcasdk/core (siempre homologación). Retorna los tipos de comprobante con campos whitelisteados; nunca genera liquidaciones ni lee credenciales.",
        "operationId": "getWslspTiposComprobante",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Tipos de comprobante según AFIP (lista vacía válida con campo `aviso` cuando homologación no informa filas)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WslspTiposComprobanteResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSLSP/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Consulta no configurada en este servidor (bootstrap sin conexión WS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wslpg/campanias": {
      "get": {
        "summary": "Consultar campañas (WSLPG)",
        "description": "Consulta de solo lectura sobre WSLPG vía @arcasdk/core (siempre homologación). Retorna las campañas con campos whitelisteados; nunca autoriza, anula, ajusta, informa ni asocia.",
        "operationId": "getWslpgCampanias",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Campañas según AFIP (lista vacía válida con campo `aviso` cuando homologación no informa filas)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WslpgCampaniasResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSLPG/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Consulta no configurada en este servidor (bootstrap sin conexión WS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wslpg/tipos-grano": {
      "get": {
        "summary": "Consultar tipos de grano (WSLPG)",
        "description": "Consulta de solo lectura sobre WSLPG vía @arcasdk/core (siempre homologación). Retorna los tipos de grano con campos whitelisteados; nunca autoriza, anula, ajusta, informa ni asocia.",
        "operationId": "getWslpgTiposGrano",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Tipos de grano según AFIP (lista vacía válida con campo `aviso` cuando homologación no informa filas)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WslpgTiposGranoResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSLPG/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Consulta no configurada en este servidor (bootstrap sin conexión WS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wslpg/grados-referencia": {
      "get": {
        "summary": "Consultar grados de referencia (WSLPG)",
        "description": "Consulta de solo lectura sobre WSLPG vía @arcasdk/core (siempre homologación). Retorna los grados de referencia con campos whitelisteados; nunca autoriza, anula, ajusta, informa ni asocia.",
        "operationId": "getWslpgGradosReferencia",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Grados de referencia según AFIP (lista vacía válida con campo `aviso` cuando homologación no informa filas)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WslpgGradosReferenciaResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSLPG/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Consulta no configurada en este servidor (bootstrap sin conexión WS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wscpe/provincias": {
      "get": {
        "summary": "Consultar provincias (WSCPE)",
        "description": "Consulta de solo lectura sobre WSCPE vía @arcasdk/core (siempre homologación). Retorna las provincias con campos whitelisteados; nunca autoriza, anula, rechaza, desvía, confirma, modifica, informa ni cierra contingencia.",
        "operationId": "getWscpeProvincias",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Provincias según AFIP (lista vacía válida con campo `aviso` cuando homologación no informa filas)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WscpeProvinciasResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSCPE/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Consulta no configurada en este servidor (bootstrap sin conexión WS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wscpe/tipos-grano": {
      "get": {
        "summary": "Consultar tipos de grano (WSCPE)",
        "description": "Consulta de solo lectura sobre WSCPE vía @arcasdk/core (siempre homologación). Retorna los tipos de grano con campos whitelisteados; nunca autoriza, anula, rechaza, desvía, confirma, modifica, informa ni cierra contingencia.",
        "operationId": "getWscpeTiposGrano",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Tipos de grano según AFIP (lista vacía válida con campo `aviso` cuando homologación no informa filas)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WscpeTiposGranoResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSCPE/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Consulta no configurada en este servidor (bootstrap sin conexión WS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/openapi.json": {
      "get": {
        "summary": "Especificación OpenAPI 3.1 en formato JSON",
        "description": "Devuelve la definición formal del contrato de la API v2 en JSON para herramientas y clientes.",
        "operationId": "getOpenApiJson",
        "security": [],
        "responses": {
          "200": {
            "description": "Especificación OpenAPI 3.1 en formato JSON",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/openapi.yaml": {
      "get": {
        "summary": "Especificación OpenAPI 3.1 en formato YAML",
        "description": "Devuelve la definición formal del contrato de la API v2 en YAML para documentación interactiva.",
        "operationId": "getOpenApiYaml",
        "security": [],
        "responses": {
          "200": {
            "description": "Especificación OpenAPI 3.1 en formato YAML",
            "content": {
              "application/yaml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/status": {
      "get": {
        "summary": "Estado del servidor WSFE (WSAA/SOAP)",
        "description": "Liveness del servidor WSFE vía @arcasdk/core. Devuelve los tres servidores que informa AFIP (`AppServer`, `DbServer`, `AuthServer`) y el ambiente del proceso. El puerto se selecciona por el ambiente de la API Key (`ambiente`: `homologacion` con una key de test, `produccion` con una de live). Nunca lee credenciales ni emite comprobantes.",
        "operationId": "getWsStatus",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Estado del servidor WSFE según AFIP",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsStatusResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSFE/AFIP no disponible o con error (detalle sanitizado)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Consulta no configurada en este servidor (bootstrap sin conexión WS)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wsfe/tipos-moneda": {
      "get": {
        "summary": "Catálogo de tipos de moneda (WSFE FEParamGetTiposMonedas)",
        "description": "Consulta de solo lectura del catálogo de monedas de WSFE vía @arcasdk/core. Devuelve las filas que AFIP retorna (`Id`, `Desc`, `FchDesde`, `FchHasta`) con campos whitelisteados; nunca emite comprobantes ni lee credenciales. El puerto se selecciona por el ambiente de la API Key (`ambiente`: `homologacion` con una key de test, `produccion` con una de live), sin fallback cruzado: si el ambiente no está cableado responde 503.",
        "operationId": "getWsfeTiposMoneda",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Monedas según AFIP (lista vacía válida con campo `aviso` cuando la respuesta válida no trae filas)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsfeTiposMonedaResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSFE/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Catálogo no configurado para el ambiente de la API Key (sin fallback cruzado entre test y live)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wsfe/tipos-iva": {
      "get": {
        "summary": "Catálogo de alícuotas de IVA (WSFE FEParamGetTiposIva)",
        "description": "Consulta de solo lectura del catálogo de alícuotas de IVA de WSFE vía @arcasdk/core. Devuelve las filas que AFIP retorna (`Id`, `Desc`, `FchDesde`, `FchHasta`) con campos whitelisteados; nunca emite comprobantes ni lee credenciales. El puerto se selecciona por el ambiente de la API Key (`ambiente`: `homologacion` con una key de test, `produccion` con una de live), sin fallback cruzado: si el ambiente no está cableado responde 503.",
        "operationId": "getWsfeTiposIva",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Alícuotas de IVA según AFIP (lista vacía válida con campo `aviso` cuando la respuesta válida no trae filas)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsfeTiposIvaResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSFE/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Catálogo no configurado para el ambiente de la API Key (sin fallback cruzado entre test y live)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wsfe/tipos-doc": {
      "get": {
        "summary": "Catálogo de tipos de documento (WSFE FEParamGetTiposDoc)",
        "description": "Consulta de solo lectura del catálogo de tipos de documento de WSFE vía @arcasdk/core. Devuelve las filas que AFIP retorna (`Id`, `Desc`, `FchDesde`, `FchHasta`) con campos whitelisteados; nunca emite comprobantes ni lee credenciales. El puerto se selecciona por el ambiente de la API Key (`ambiente`: `homologacion` con una key de test, `produccion` con una de live), sin fallback cruzado: si el ambiente no está cableado responde 503.",
        "operationId": "getWsfeTiposDoc",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Tipos de documento según AFIP (lista vacía válida con campo `aviso` cuando la respuesta válida no trae filas)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsfeTiposDocResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSFE/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Catálogo no configurado para el ambiente de la API Key (sin fallback cruzado entre test y live)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wsfe/tipos-concepto": {
      "get": {
        "summary": "Catálogo de tipos de concepto (WSFE FEParamGetTiposConcepto)",
        "description": "Consulta de solo lectura del catálogo de tipos de concepto de WSFE vía @arcasdk/core. Devuelve las filas que AFIP retorna (`Id`, `Desc`, `FchDesde`, `FchHasta`) con campos whitelisteados; nunca emite comprobantes ni lee credenciales. El puerto se selecciona por el ambiente de la API Key (`ambiente`: `homologacion` con una key de test, `produccion` con una de live), sin fallback cruzado: si el ambiente no está cableado responde 503.",
        "operationId": "getWsfeTiposConcepto",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Tipos de concepto según AFIP (lista vacía válida con campo `aviso` cuando la respuesta válida no trae filas)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsfeTiposConceptoResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSFE/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Catálogo no configurado para el ambiente de la API Key (sin fallback cruzado entre test y live)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wsfe/tipos-comprobante": {
      "get": {
        "summary": "Catálogo de tipos de comprobante (WSFE FEParamGetTiposCbte)",
        "description": "Consulta de solo lectura del catálogo de tipos de comprobante de WSFE (FEParamGetTiposCbte) vía @arcasdk/core. Es el set del MISMO webservice que la emisión, distinto del `wslsp/tipos-comprobante` (hacienda). Devuelve las filas que AFIP retorna (`Id`, `Desc`, `FchDesde`, `FchHasta`) con campos whitelisteados; nunca emite comprobantes ni lee credenciales. El puerto se selecciona por el ambiente de la API Key (`ambiente`: `homologacion` con una key de test, `produccion` con una de live), sin fallback cruzado: si el ambiente no está cableado responde 503.",
        "operationId": "getWsfeTiposComprobante",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Tipos de comprobante según AFIP (lista vacía válida con campo `aviso` cuando la respuesta válida no trae filas)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsfeTiposComprobanteResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSFE/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Catálogo no configurado para el ambiente de la API Key (sin fallback cruzado entre test y live)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wsfe/actividades": {
      "get": {
        "summary": "Catálogo de actividades (WSFE FEParamGetActividades)",
        "description": "Consulta de solo lectura del catálogo de actividades de WSFE vía @arcasdk/core. Devuelve las filas que AFIP retorna (`Id`, `Orden`, `Desc`) con campos whitelisteados, incluido `orden`; nunca emite comprobantes ni lee credenciales. El puerto se selecciona por el ambiente de la API Key (`ambiente`: `homologacion` con una key de test, `produccion` con una de live), sin fallback cruzado: si el ambiente no está cableado responde 503.",
        "operationId": "getWsfeActividades",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Actividades según AFIP (lista vacía válida con campo `aviso` cuando la respuesta válida no trae filas)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsfeActividadesResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSFE/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Catálogo no configurado para el ambiente de la API Key (sin fallback cruzado entre test y live)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wsfe/paises": {
      "get": {
        "summary": "Catálogo de países (WSFE FEParamGetTiposPaises)",
        "description": "Consulta de solo lectura del catálogo de países de WSFE vía @arcasdk/core. Devuelve las filas que AFIP retorna (`Id`, `Desc`) con campos whitelisteados; este set no informa vigencias. Nunca emite comprobantes ni lee credenciales. El puerto se selecciona por el ambiente de la API Key (`ambiente`: `homologacion` con una key de test, `produccion` con una de live), sin fallback cruzado: si el ambiente no está cableado responde 503.",
        "operationId": "getWsfePaises",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Países según AFIP (lista vacía válida con campo `aviso` cuando la respuesta válida no trae filas)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsfePaisesResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSFE/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Catálogo no configurado para el ambiente de la API Key (sin fallback cruzado entre test y live)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wsfe/tipos-tributo": {
      "get": {
        "summary": "Catálogo de tipos de tributo (WSFE FEParamGetTiposTributos)",
        "description": "Consulta de solo lectura del catálogo de tributos de WSFE vía @arcasdk/core. Devuelve las filas que AFIP retorna (`Id`, `Desc`, `FchDesde`, `FchHasta`) con campos whitelisteados; nunca emite comprobantes ni lee credenciales. El puerto se selecciona por el ambiente de la API Key (`ambiente`: `homologacion` con una key de test, `produccion` con una de live), sin fallback cruzado: si el ambiente no está cableado responde 503.",
        "operationId": "getWsfeTiposTributo",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Tipos de tributo según AFIP (lista vacía válida con campo `aviso` cuando la respuesta válida no trae filas)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsfeTiposTributoResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSFE/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Catálogo no configurado para el ambiente de la API Key (sin fallback cruzado entre test y live)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wsfe/tipos-iva-receptor": {
      "get": {
        "summary": "Catálogo de condiciones de IVA del receptor (WSFE FEParamGetCondicionIvaReceptor)",
        "description": "Consulta de solo lectura del catálogo de condiciones de IVA del receptor de WSFE vía @arcasdk/core (la operación AFIP es FEParamGetCondicionIvaReceptor; el SDK la expone como `getIvaReceptorTypes`). Devuelve las filas que AFIP retorna (`Id`, `Desc`, `Cmp_Clase`) con campos whitelisteados. Se llama sin el filtro opcional `ClaseCmp` del SDK: el catálogo se expone completo y el consumidor filtra; no se inventa ningún valor de filtro. Nunca emite comprobantes ni lee credenciales. El puerto se selecciona por el ambiente de la API Key (`ambiente`: `homologacion` con una key de test, `produccion` con una de live), sin fallback cruzado: si el ambiente no está cableado responde 503.",
        "operationId": "getWsfeTiposIvaReceptor",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Condiciones de IVA del receptor según AFIP (lista vacía válida con campo `aviso` cuando la respuesta válida no trae filas)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsfeTiposIvaReceptorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSFE/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Catálogo no configurado para el ambiente de la API Key (sin fallback cruzado entre test y live)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ws/wsfe/tipos-opcional": {
      "get": {
        "summary": "Catálogo de tipos de dato opcional (WSFE FEParamGetTiposOpcional)",
        "description": "Consulta de solo lectura del catálogo de datos opcionales de WSFE vía @arcasdk/core. Devuelve las filas que AFIP retorna (`Id`, `Desc`, `FchDesde`, `FchHasta`) con campos whitelisteados; nunca emite comprobantes ni lee credenciales. El puerto se selecciona por el ambiente de la API Key (`ambiente`: `homologacion` con una key de test, `produccion` con una de live), sin fallback cruzado: si el ambiente no está cableado responde 503.",
        "operationId": "getWsfeTiposOpcional",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Tipos de dato opcional según AFIP (lista vacía válida con campo `aviso` cuando la respuesta válida no trae filas)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsfeTiposOpcionalResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Proveedor WSFE/AFIP no disponible o con error (detalle sanitizado; solo fallas genuinas: una respuesta válida vacía es 200 con `aviso`, nunca 502)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Catálogo no configurado para el ambiente de la API Key (sin fallback cruzado entre test y live)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/admin/tenants": {
      "post": {
        "summary": "Crear un tenant (plano de administración)",
        "description": "Plano de administración, sólo para el operador. Requiere `Authorization: Bearer <ADMIN_API_KEY>`, un secreto DISTINTO de la API key de tenant (el proceso no arranca si son iguales). Si el proceso no tiene configurada la `ADMIN_API_KEY`, estas rutas responden `503 admin_no_configurado`.",
        "operationId": "adminCrearTenant",
        "security": [
          {
            "AdminBearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AdminCrearTenantRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Tenant creado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminTenantResponse"
                }
              }
            }
          },
          "400": {
            "description": "id/nombre faltantes, o id que no cumple `^[A-Za-z0-9_-]{1,64}$`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Bearer del plano de administración ausente o inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "El tenant ya existe",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Plano de administración no configurado (sin `ADMIN_API_KEY`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "Listar tenants (plano de administración)",
        "description": "Devuelve todos los tenants con la cantidad de claves emitidas (incluidas las revocadas).",
        "operationId": "adminListarTenants",
        "security": [
          {
            "AdminBearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Tenants con su conteo de claves",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminTenantsListResponse"
                }
              }
            }
          },
          "401": {
            "description": "Bearer del plano de administración ausente o inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Plano de administración no configurado (sin `ADMIN_API_KEY`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/admin/tenants/{tenant_id}/keys": {
      "get": {
        "summary": "Listar claves de un tenant (plano de administración)",
        "description": "Devuelve metadatos de claves; nunca el texto plano.",
        "operationId": "adminListarApiKeys",
        "security": [
          {
            "AdminBearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AdminTenantId"
          }
        ],
        "responses": {
          "200": {
            "description": "Metadatos de las claves del tenant",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminApiKeysListResponse"
                }
              }
            }
          },
          "401": {
            "description": "Bearer del plano de administración ausente o inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "El tenant no existe",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Plano de administración no configurado (sin `ADMIN_API_KEY`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Emitir una API key de tenant (plano de administración)",
        "description": "Emite una clave para el tenant. El texto plano (`test_sec_…`/`live_sec_…`) se devuelve EXACTAMENTE UNA VEZ en este 201; el servidor sólo persiste su hash SHA-256.",
        "operationId": "adminEmitirApiKey",
        "security": [
          {
            "AdminBearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/AdminTenantId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AdminEmitirApiKeyRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Clave emitida (texto plano return-once)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminApiKeyIssuedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Cuerpo inválido, o `ambiente` distinto de `test`/`live`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Bearer del plano de administración ausente o inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "El tenant no existe",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "El hash de la clave ya existe",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Plano de administración no configurado (sin `ADMIN_API_KEY`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/admin/keys/{key_hash}/revoke": {
      "post": {
        "summary": "Revocar una API key (plano de administración)",
        "description": "Desactiva la clave por su hash SHA-256 (64 hex). Revocar es inmediato.",
        "operationId": "adminRevocarApiKey",
        "security": [
          {
            "AdminBearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "key_hash",
            "in": "path",
            "required": true,
            "description": "Hash SHA-256 de la clave (64 hex minúscula).",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Clave revocada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminRevokeResponse"
                }
              }
            }
          },
          "400": {
            "description": "El hash no son 64 hex minúscula",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Bearer del plano de administración ausente o inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "El hash no existe",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Plano de administración no configurado (sin `ADMIN_API_KEY`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/admin/metering": {
      "get": {
        "summary": "Totales de uso (plano de administración)",
        "description": "Totales de `arca_metering` del tenant, con filtros opcionales de company y ambiente.",
        "operationId": "adminMetering",
        "security": [
          {
            "AdminBearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "tenant_id",
            "in": "query",
            "required": true,
            "description": "Tenant cuyos totales se consultan.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "company_id",
            "in": "query",
            "required": false,
            "description": "Filtro opcional por empresa (sub-cuenta de plataforma).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ambiente",
            "in": "query",
            "required": false,
            "description": "Filtro opcional por ambiente.",
            "schema": {
              "type": "string",
              "enum": [
                "test",
                "live"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Totales de uso",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminMeteringResponse"
                }
              }
            }
          },
          "400": {
            "description": "Falta `tenant_id`, o `ambiente` distinto de `test`/`live`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Bearer del plano de administración ausente o inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Plano de administración no configurado (sin `ADMIN_API_KEY`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sag/localidades": {
      "get": {
        "summary": "Catálogo de localidades de La Pampa (SAG)",
        "description": "Devuelve el catálogo oficial de códigos de localidad y municipios de la Provincia de La Pampa configurados en el Sistema de Administración de Guías (SAG).",
        "operationId": "getSagLocalidades",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Lista de localidades de La Pampa obtenida exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SagLocalidadesResponse"
                }
              }
            }
          },
          "401": {
            "description": "Token API ausente, inválido o malformado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sag/saldo": {
      "post": {
        "summary": "Consulta de saldo en cuenta corriente municipal (SAG)",
        "description": "Obtiene el saldo disponible en la cuenta corriente municipal de SAG La Pampa para un CUIT y municipio especificados. Requiere autenticación de tenant y credenciales de SAG (clave cifrada mediante RSA-OAEP-SHA256 con la clave pública de la API, o en texto plano).",
        "operationId": "getSagSaldo",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SagSaldoRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saldo obtenido exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SagSaldoResponse"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros requeridos faltantes o credencial inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SagErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Token API ausente, inválido o malformado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error de negocio retornado por el servidor de SAG La Pampa",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SagErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sag/movimientos": {
      "post": {
        "summary": "Consulta de movimientos de cuenta corriente municipal (SAG)",
        "description": "Obtiene la lista histórica de movimientos registrados en el año (guías emitidas, pagos y débitos) en la cuenta corriente del municipio en SAG La Pampa.",
        "operationId": "getSagMovimientos",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SagMovimientosRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Movimientos obtenidos exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SagMovimientosResponse"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros requeridos faltantes o credencial inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SagErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Token API ausente, inválido o malformado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error de negocio retornado por el servidor de SAG La Pampa",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SagErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sag/guias/cotizar": {
      "post": {
        "summary": "Cotización y liquidación de guía de ganado (SAG)",
        "description": "Inicia un borrador temporal en SAG La Pampa, carga las tropas de animales especificadas, aplica el destino y devuelve la liquidación discriminada entre la tasa de guía municipal y el anticipo de Ingresos Brutos de Rentas de La Pampa.",
        "operationId": "cotizarSagGuia",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SagCotizacionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cotización calculada exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SagCotizacionResponse"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros requeridos faltantes o credencial inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SagErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Token API ausente, inválido o malformado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error de negocio retornado por el servidor de SAG La Pampa",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SagErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/dgr/inmuebles/{partida}": {
      "get": {
        "summary": "Consulta pública de partida inmobiliaria",
        "description": "Devuelve datos catastrales, superficie, CUIT del titular y deuda desglosada cuota a cuota.",
        "operationId": "consultarPartidaDgr",
        "tags": [
          "DGR La Pampa"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "partida",
            "in": "path",
            "required": true,
            "description": "Número de partida inmobiliaria",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "fechaInteres",
            "in": "query",
            "required": false,
            "description": "Fecha de cálculo de intereses (DD/MM/AAAA)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Detalle de partida y deuda",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos"
          },
          "401": {
            "description": "No autorizado"
          },
          "422": {
            "description": "Error reportado por el portal DGR"
          },
          "500": {
            "description": "Error interno"
          }
        }
      }
    },
    "/api/v2/dgr/vehiculos/{dominio}": {
      "get": {
        "summary": "Consulta pública de patente vehicular",
        "description": "Devuelve modelo/año, CUIT del titular, cuotas impagas e ID de liquidación para Red Link.",
        "operationId": "consultarPatenteDgr",
        "tags": [
          "DGR La Pampa"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "dominio",
            "in": "path",
            "required": true,
            "description": "Dominio o patente del vehículo",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "fechaInteres",
            "in": "query",
            "required": false,
            "description": "Fecha de cálculo de intereses (DD/MM/AAAA)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Detalle de vehículo y deuda",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos"
          },
          "401": {
            "description": "No autorizado"
          },
          "422": {
            "description": "Error reportado por el portal DGR"
          },
          "500": {
            "description": "Error interno"
          }
        }
      }
    },
    "/api/v2/dgr/resumen": {
      "post": {
        "summary": "Resumen consolidado de deuda del contribuyente",
        "description": "Inicia sesión con CAIP y obtiene los totales consolidados por vehículos, inmuebles, IIBB, financiación y apremios.",
        "operationId": "obtenerResumenDeudaDgr",
        "tags": [
          "DGR La Pampa"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "cuit",
                  "clave"
                ],
                "properties": {
                  "cuit": {
                    "type": "string"
                  },
                  "clave": {
                    "type": "string"
                  },
                  "fechaInteres": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resumen consolidado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos"
          },
          "401": {
            "description": "No autorizado"
          },
          "422": {
            "description": "Credenciales CAIP inválidas o error DGR"
          },
          "500": {
            "description": "Error interno"
          }
        }
      }
    },
    "/api/v2/dgr/bienes": {
      "post": {
        "summary": "Padrón completo de bienes asociados al CUIT",
        "description": "Obtiene el listado completo de patentes, partidas inmobiliarias con nomenclatura y cuentas de IIBB.",
        "operationId": "obtenerBienesDgr",
        "tags": [
          "DGR La Pampa"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "cuit",
                  "clave"
                ],
                "properties": {
                  "cuit": {
                    "type": "string"
                  },
                  "clave": {
                    "type": "string"
                  },
                  "fechaInteres": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Padrón de bienes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos"
          },
          "401": {
            "description": "No autorizado"
          },
          "422": {
            "description": "Credenciales CAIP inválidas o error DGR"
          },
          "500": {
            "description": "Error interno"
          }
        }
      }
    },
    "/api/v2/dgr/cuotas": {
      "post": {
        "summary": "Desglose de cuotas período a período",
        "description": "Obtiene las cuotas, vencimientos, intereses y subtotales de una cuenta o bien específico.",
        "operationId": "obtenerCuotasDgr",
        "tags": [
          "DGR La Pampa"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "cuit",
                  "clave",
                  "impuesto",
                  "idCuenta"
                ],
                "properties": {
                  "cuit": {
                    "type": "string"
                  },
                  "clave": {
                    "type": "string"
                  },
                  "impuesto": {
                    "type": "string",
                    "enum": [
                      "Vehiculos",
                      "Inmobiliario"
                    ]
                  },
                  "idCuenta": {
                    "type": "string"
                  },
                  "fechaInteres": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Lista de cuotas",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos"
          },
          "401": {
            "description": "No autorizado"
          },
          "422": {
            "description": "Error DGR"
          },
          "500": {
            "description": "Error interno"
          }
        }
      }
    },
    "/api/v2/dgr/riesgo-fiscal": {
      "post": {
        "summary": "Consulta de Nivel de Riesgo Fiscal",
        "description": "Obtiene el nivel de riesgo fiscal (Bajo, Medio, Alto) y estado ante Rentas La Pampa.",
        "operationId": "obtenerRiesgoFiscalDgr",
        "tags": [
          "DGR La Pampa"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "cuit",
                  "clave"
                ],
                "properties": {
                  "cuit": {
                    "type": "string"
                  },
                  "clave": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Nivel de riesgo fiscal",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Parámetros inválidos"
          },
          "401": {
            "description": "No autorizado"
          },
          "422": {
            "description": "Error DGR"
          },
          "500": {
            "description": "Error interno"
          }
        }
      }
    },
    "/api/v1/srt/expedientes": {
      "post": {
        "summary": "Expedientes de casos de SRT (Superintendencia de Riesgos del Trabajo)",
        "description": "Lista los expedientes de caso del representado en el portal de SRT, con su OID, número, fecha de inicio, motivo y damnificado. La credencial es la del PORTAL ARCA (CUIT + clave fiscal): SRT no tiene login propio y la sesión se abre por el SSO de ARCA. Sólo lectura.",
        "operationId": "getSrtExpedientes",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SrtExpedientesRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Expedientes del representado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SrtExpedientesResponse"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros requeridos faltantes, credencial inválida (`credencial_invalida`), clave fiscal en claro en un despliegue que no la habilita (`credencial_en_claro_no_permitida`) o filtro con formato inválido (`filtro_invalido`: `numExpdte` sólo con la parte numérica y `numAnio` de cuatro dígitos)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SrtErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Token API ausente, inválido o malformado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error de negocio retornado por el portal de SRT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SrtErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/srt/expedientes/{oid}/comunicaciones": {
      "post": {
        "summary": "Notificaciones de un expediente de SRT",
        "description": "Lista las notificaciones de un expediente: el motivo de cada una (su tipo de comunicación), la fecha de notificación, el remitente, el sector y el último estado. El listado del portal es un postback de WebForms: el driver toma el ViewState de la página de filtro.",
        "operationId": "getSrtComunicaciones",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "oid",
            "in": "path",
            "required": true,
            "description": "OID del expediente",
            "schema": {
              "type": "integer"
            },
            "example": 5972601
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SrtCredencialRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Notificaciones del expediente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SrtComunicacionesResponse"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros requeridos faltantes o credencial inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SrtErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Token API ausente, inválido o malformado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error de negocio retornado por el portal de SRT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SrtErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/srt/comunicaciones/{tra_id}": {
      "post": {
        "summary": "Detalle de una notificación de SRT",
        "description": "Detalle de una notificación: su tipo, fecha, remitente, el texto de la comunicación y los archivos adjuntos (el PDF de la notificación) con su identificador de descarga.",
        "operationId": "getSrtDetalleComunicacion",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "tra_id",
            "in": "path",
            "required": true,
            "description": "Identificador de la notificación (`traID` del portal)",
            "schema": {
              "type": "string"
            },
            "example": "269201865"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SrtCredencialRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Detalle de la notificación",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SrtDetalleResponse"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros requeridos faltantes o credencial inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SrtErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Token API ausente, inválido o malformado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error de negocio retornado por el portal de SRT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SrtErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/srt/adjuntos/{id}": {
      "post": {
        "summary": "Descarga del PDF de una notificación de SRT",
        "description": "Descarga el archivo adjunto de una notificación (el PDF emitido por el portal). Es opt-in: el dueño lo marcó como prescindible, por eso va en su propio endpoint y no en el detalle de la notificación.",
        "operationId": "getSrtAdjunto",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del adjunto (el `id` de la tabla de adjuntos)",
            "schema": {
              "type": "integer"
            },
            "example": 135128843
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SrtCredencialRequest"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "nombre": {
                        "type": "string",
                        "description": "Nombre del archivo (el portal lo usa en la ruta de descarga)",
                        "example": "NotificacionCitacion.pdf"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Binario del adjunto",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros requeridos faltantes o credencial inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SrtErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Token API ausente, inválido o malformado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error de negocio retornado por el portal de SRT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SrtErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/srt/catalogos": {
      "post": {
        "summary": "Catálogos del portal de SRT",
        "description": "Catálogos del servicio: los tipos de ingreso (el motivo del alta de un expediente). Sólo lectura.",
        "operationId": "getSrtCatalogos",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SrtCredencialRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Catálogos del servicio",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SrtCatalogosResponse"
                }
              }
            }
          },
          "400": {
            "description": "Parámetros requeridos faltantes o credencial inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SrtErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Token API ausente, inválido o malformado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Error de negocio retornado por el portal de SRT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SrtErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Token API provisto en la cabecera Authorization: Bearer <api-key>"
      },
      "AdminBearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Secreto del plano de administración (ADMIN_API_KEY), distinto de la API key de tenant. Plano separado: la clave admin no abre rutas de tenant y viceversa."
      }
    },
    "parameters": {
      "AdminTenantId": {
        "name": "tenant_id",
        "in": "path",
        "required": true,
        "description": "Identificador del tenant (`^[A-Za-z0-9_-]{1,64}$`).",
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]{1,64}$"
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Token API ausente, inválido o malformado",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string",
                  "example": "unauthorized"
                }
              },
              "required": [
                "error"
              ]
            }
          }
        }
      },
      "InternalError": {
        "description": "Error interno no controlado en el servidor",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "ForbiddenAmbiente": {
        "description": "La API key es válida pero pertenece al otro ambiente (una key de test contra un despliegue live o al revés). Guardia de ambiente del servidor, previa a cualquier lógica de jobs.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AmbienteInvalidoResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "PublicKeyResponse": {
        "type": "object",
        "properties": {
          "algoritmo": {
            "type": "string",
            "example": "RSA-OAEP-SHA256"
          },
          "formato": {
            "type": "string",
            "example": "base64"
          },
          "public_key_pem": {
            "type": "string",
            "description": "Clave pública RSA en formato PEM",
            "example": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...\n-----END PUBLIC KEY-----\n"
          }
        },
        "required": [
          "algoritmo",
          "formato",
          "public_key_pem"
        ]
      },
      "CrearJobRequest": {
        "type": "object",
        "properties": {
          "cuit_representante": {
            "type": "string",
            "pattern": "^\\d{11}$",
            "description": "CUIT fiscal (11 dígitos, sin guiones) que inicia sesión y autentica contra ARCA.",
            "example": "20123456780"
          },
          "cuit_representado": {
            "type": "string",
            "pattern": "^\\d{11}$",
            "description": "CUIT del contribuyente consultado (puede ser el mismo que representante).",
            "example": "30876543210"
          },
          "clave_encriptada": {
            "type": "string",
            "description": "Clave fiscal de ARCA cifrada en base64 con RSA-OAEP-SHA256 usando la clave pública vigente.",
            "example": "Q2xhdmVGaXNjYWxTZWNyZXRhMQ=="
          },
          "desde": {
            "type": "string",
            "pattern": "^\\d{2}/\\d{2}/\\d{4}$",
            "description": "Fecha de inicio en formato DD/MM/AAAA.",
            "example": "01/08/2026"
          },
          "hasta": {
            "type": "string",
            "pattern": "^\\d{2}/\\d{2}/\\d{4}$",
            "description": "Fecha de fin en formato DD/MM/AAAA. La ventana total no debe superar los 365 días.",
            "example": "14/09/2026"
          },
          "opciones": {
            "type": "object",
            "description": "Selección de servicios. `tipo` (legacy, un servicio) y `servicios` (varios, un solo login) son excluyentes: juntos responden 400 `parametros_invalidos`. Sin ninguno vale el default `mc_recibidos`.",
            "properties": {
              "tipo": {
                "type": "string",
                "enum": [
                  "mc_recibidos",
                  "mc_emitidos",
                  "lsp_liquidaciones",
                  "rcel",
                  "srt_expedientes",
                  "srt_expedientes_historico",
                  "srt_comunicaciones"
                ],
                "default": "mc_recibidos",
                "description": "Servicio de ARCA a consultar (forma legacy: un solo servicio). Se sigue aceptando sin cambios; no combinar con `servicios`. El enum publicado espeja el registro de tipos de consulta del servicio: un tipo ausente del registro falla cerrado con `servicio_no_soportado`. `srt_expedientes` trae los casos (expedientes) ACTIVOS del representado, `srt_expedientes_historico` los casos históricos y `srt_comunicaciones` sus notificaciones; los tres comparten la MISMA forma de fila (el origen lo da el nombre del servicio, no la fila). SRT no tiene ventana: el portal lista los casos sin filtro de fechas, así que `desde`/`hasta` se aceptan y se ignoran.",
                "example": "mc_recibidos"
              },
              "servicios": {
                "type": "array",
                "minItems": 1,
                "maxItems": 4,
                "description": "Servicios a extraer con un solo login, en el orden pedido (1 a 4 elementos). Cada elemento sin `desde`/`hasta` hereda la ventana del nivel superior (`desde`/`hasta` siguen obligatorios y son el default por servicio). Un `tipo` repetido, un elemento no-objeto o una ventana por elemento inválida responden 400 `parametros_invalidos`; un `tipo` desconocido responde 400 `servicio_no_soportado`. Con resultados mixtos el job agrega a `parcial` (ver `GET /api/v2/jobs/{job_id}`).",
                "items": {
                  "type": "object",
                  "properties": {
                    "tipo": {
                      "type": "string",
                      "enum": [
                        "mc_recibidos",
                        "mc_emitidos",
                        "lsp_liquidaciones",
                        "rcel",
                        "srt_expedientes",
                        "srt_expedientes_historico",
                        "srt_comunicaciones"
                      ],
                      "description": "Servicio de ARCA a consultar (mismo enum que `tipo`).",
                      "example": "mc_recibidos"
                    },
                    "desde": {
                      "type": "string",
                      "pattern": "^\\d{2}/\\d{2}/\\d{4}$",
                      "description": "Inicio opcional en formato DD/MM/AAAA; si falta hereda el `desde` del nivel superior. La ventana propia no debe superar los 365 días.",
                      "example": "01/08/2026"
                    },
                    "hasta": {
                      "type": "string",
                      "pattern": "^\\d{2}/\\d{2}/\\d{4}$",
                      "description": "Fin opcional en formato DD/MM/AAAA; si falta hereda el `hasta` del nivel superior. La ventana propia no debe superar los 365 días.",
                      "example": "08/10/2026"
                    }
                  },
                  "required": [
                    "tipo"
                  ]
                }
              }
            }
          }
        },
        "required": [
          "cuit_representante",
          "cuit_representado",
          "clave_encriptada",
          "desde",
          "hasta"
        ]
      },
      "CrearJobResponse": {
        "type": "object",
        "properties": {
          "job_id": {
            "type": "string",
            "description": "Identificador único asignado al job encolado.",
            "example": "job_a1b2c3d4e5f60718"
          },
          "estado": {
            "$ref": "#/components/schemas/JobEstado"
          },
          "replay": {
            "type": "boolean",
            "description": "Presente sólo cuando el request llevaba `Idempotency-Key`: `false` en una creación nueva y `true` en el replay del job original (misma clave, mismo payload). Nunca re-ejecuta nada.",
            "example": false
          },
          "reintento_de": {
            "type": "string",
            "description": "Presente sólo en POST /api/v2/jobs/{job_id}/retry: el `job_id` original que este job reintenta.",
            "example": "job_a1b2c3d4e5f60718"
          }
        },
        "required": [
          "job_id",
          "estado"
        ]
      },
      "ListaJobsResponse": {
        "type": "object",
        "description": "Página de jobs del tenant de la credencial. `total` es el total del filtro (no de la página) y sólo cuenta jobs del tenant.",
        "properties": {
          "jobs": {
            "type": "array",
            "description": "Jobs del tenant, del más nuevo al más viejo.",
            "items": {
              "$ref": "#/components/schemas/JobResponse"
            }
          },
          "total": {
            "type": "integer",
            "minimum": 0,
            "description": "Cantidad de jobs del tenant que cumplen los filtros.",
            "example": 3
          },
          "limite": {
            "type": "integer",
            "description": "Tamaño de página efectivamente aplicado.",
            "example": 20
          },
          "desplazamiento": {
            "type": "integer",
            "description": "Offset de página efectivamente aplicado.",
            "example": 0
          }
        },
        "required": [
          "jobs",
          "total",
          "limite",
          "desplazamiento"
        ]
      },
      "ReintentarJobRequest": {
        "type": "object",
        "description": "El retry no acepta el payload operativo: TODOS los servicios del job original (cada uno con su ventana propia, en orden), CUITs y envelope salen del job original. Lo único que se pide es la clave fiscal re-cifrada, porque este servicio no la custodia.",
        "properties": {
          "clave_encriptada": {
            "type": "string",
            "description": "Clave fiscal de ARCA cifrada en base64 con RSA-OAEP-SHA256 usando la clave pública vigente.",
            "example": "Q2xhdmVGaXNjYWxTZWNyZXRhMQ=="
          }
        },
        "required": [
          "clave_encriptada"
        ]
      },
      "JobMotivoError": {
        "type": "string",
        "enum": [
          "credencial_invalida",
          "captcha",
          "transporte",
          "portal_rechazo",
          "guardia_conteo",
          "servicio_no_soportado",
          "parametros_invalidos",
          "storage",
          "interno",
          "idempotency_key_requerida",
          "idempotency_conflict_payload",
          "idempotency_in_flight",
          "intento_rechazado"
        ],
        "description": "Taxonomía cerrada del carril de jobs: la del motor (`MotivoError`) más los códigos propios de la idempotencia de creación, espejo de la tabla de decisión de idempotencia del servicio. - idempotency_key_requerida: el header `Idempotency-Key` viajó presente pero vacío (400). - idempotency_conflict_payload: la misma clave de idempotencia se reutilizó con otro payload (409). - idempotency_in_flight: la misma clave tiene una creación de job en curso (409). - intento_rechazado: la clave de idempotencia ya fue rechazada (422).",
        "example": "parametros_invalidos"
      },
      "JobErrorResponse": {
        "type": "object",
        "properties": {
          "motivo": {
            "$ref": "#/components/schemas/JobMotivoError"
          }
        },
        "required": [
          "motivo"
        ]
      },
      "AmbienteInvalidoMotivo": {
        "type": "string",
        "enum": [
          "api_key_ambiente_invalido"
        ],
        "description": "Guardia de ambiente del proceso del servicio: la clave es válida pero es del otro ambiente. No pertenece a la taxonomía del motor ni a la de la idempotencia.",
        "example": "api_key_ambiente_invalido"
      },
      "AmbienteInvalidoResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "example": "forbidden"
          },
          "motivo": {
            "$ref": "#/components/schemas/AmbienteInvalidoMotivo"
          },
          "esperado": {
            "type": "string",
            "description": "Ambiente que exige este despliegue.",
            "example": "test"
          },
          "recibido": {
            "type": "string",
            "description": "Ambiente de la API key presentada.",
            "example": "live"
          }
        },
        "required": [
          "error",
          "motivo",
          "esperado",
          "recibido"
        ]
      },
      "MotivoError": {
        "type": "string",
        "enum": [
          "credencial_invalida",
          "captcha",
          "transporte",
          "portal_rechazo",
          "guardia_conteo",
          "servicio_no_soportado",
          "parametros_invalidos",
          "storage",
          "interno"
        ],
        "description": "Taxonomía cerrada de motivos de error: - credencial_invalida: Clave fiscal rechazada por ARCA o descifrado fallido. - captcha: Detección de desafío captcha interactivo en portal ARCA. - transporte: Timeout de red, conexión interrumpida o sesión expirada con ARCA. - portal_rechazo: ARCA rechazó el acceso al servicio por falta de autorización/delegación. - guardia_conteo: Discrepancia entre comprobantes declarados y obtenidos en descarga (fallo de completitud). - servicio_no_soportado: Driver o tipo de consulta no registrado en el motor. - parametros_invalidos: CUIT inválido, rango de fechas > 365 días o payload malformado. - storage: Fallo al persistir artefactos o datasets. - interno: Error no contemplado en la capa de ejecución.",
        "example": "parametros_invalidos"
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "motivo": {
            "$ref": "#/components/schemas/MotivoError"
          }
        },
        "required": [
          "motivo"
        ]
      },
      "JobEstado": {
        "type": "string",
        "enum": [
          "encolado",
          "ejecutando",
          "completado",
          "parcial",
          "requiere_humano",
          "fallido"
        ],
        "description": "Estado consolidado del ciclo de vida del job.",
        "example": "completado"
      },
      "ServicioEstado": {
        "type": "string",
        "enum": [
          "pendiente",
          "completado",
          "requiere_humano",
          "fallido",
          "sin_datos"
        ],
        "description": "Estado de ejecución del servicio individual dentro del job.",
        "example": "completado"
      },
      "LoginEstado": {
        "type": "string",
        "enum": [
          "pendiente",
          "ok",
          "fallido"
        ],
        "description": "Estado de la autenticación de sesión con ARCA. Queda `pendiente` cuando todos los servicios del job se sirvieron del cache de artefacto: el job nunca abrió sesión ni transmitió la clave fiscal.",
        "example": "ok"
      },
      "Conteo": {
        "type": "object",
        "properties": {
          "declarado": {
            "type": "integer",
            "minimum": 0,
            "description": "Total de comprobantes informados por ARCA en el período.",
            "example": 10
          },
          "obtenido": {
            "type": "integer",
            "minimum": 0,
            "description": "Total de comprobantes efectivamente descargados y verificados.",
            "example": 10
          }
        },
        "required": [
          "declarado",
          "obtenido"
        ]
      },
      "AlicuotaMc": {
        "type": "object",
        "properties": {
          "rate": {
            "type": "number",
            "description": "Porcentaje de alícuota de IVA aplicada (ej. 21, 10.5, 27).",
            "example": 21
          },
          "neto": {
            "type": "number",
            "description": "Base neta gravada sujeta a esta alícuota.",
            "example": 1000
          },
          "iva": {
            "type": "number",
            "description": "Importe liquidado de IVA correspondiente.",
            "example": 210
          }
        },
        "required": [
          "rate",
          "neto",
          "iva"
        ]
      },
      "ComprobanteMc": {
        "type": "object",
        "description": "Comprobante fiscal normalizado emitido o recibido vía Mis Comprobantes.",
        "properties": {
          "fecha": {
            "type": "string",
            "format": "date",
            "description": "Fecha de emisión (YYYY-MM-DD).",
            "example": "2026-08-01"
          },
          "tipoAfip": {
            "type": "string",
            "description": "Código numérico de tipo de comprobante AFIP (1=Factura A, 6=Factura B, etc.).",
            "example": "1"
          },
          "puntoVenta": {
            "type": "string",
            "description": "Punto de venta del comprobante.",
            "example": "1"
          },
          "numero": {
            "type": "string",
            "description": "Número de comprobante emitido.",
            "example": "100"
          },
          "cae": {
            "type": [
              "string",
              "null"
            ],
            "description": "Código de Autorización Electrónico asignado por ARCA, o null si no aplica.",
            "example": "12345678901234"
          },
          "emisorCuit": {
            "type": "string",
            "description": "CUIT del emisor del comprobante.",
            "example": "30765432109"
          },
          "emisorNombre": {
            "type": "string",
            "description": "Razón social o denominación del emisor.",
            "example": "EMISOR S.A."
          },
          "moneda": {
            "type": "string",
            "enum": [
              "ARS",
              "USD"
            ],
            "description": "Moneda de facturación.",
            "example": "ARS"
          },
          "tipoCambio": {
            "type": "number",
            "description": "Tipo de cambio aplicado respecto al peso argentino.",
            "example": 1
          },
          "netoGravado": {
            "type": "number",
            "description": "Subtotal neto gravado total.",
            "example": 1000
          },
          "netoNoGravado": {
            "type": "number",
            "description": "Subtotal neto no gravado.",
            "example": 0
          },
          "exentas": {
            "type": "number",
            "description": "Importe de operaciones exentas.",
            "example": 0
          },
          "otrosTributos": {
            "type": "number",
            "description": "Importe de otros tributos y percepciones no IVA.",
            "example": 0
          },
          "totalIva": {
            "type": "number",
            "description": "Importe total de IVA liquidado.",
            "example": 210
          },
          "total": {
            "type": "number",
            "description": "Importe total facturado del comprobante.",
            "example": 1210
          },
          "alicuotas": {
            "type": "array",
            "description": "Desglose discriminado de alícuotas de IVA.",
            "items": {
              "$ref": "#/components/schemas/AlicuotaMc"
            }
          }
        },
        "required": [
          "fecha",
          "tipoAfip",
          "puntoVenta",
          "numero",
          "emisorCuit",
          "emisorNombre",
          "moneda",
          "tipoCambio",
          "netoGravado",
          "netoNoGravado",
          "exentas",
          "otrosTributos",
          "totalIva",
          "total",
          "alicuotas"
        ]
      },
      "ItemHaciendaLsp": {
        "type": "object",
        "description": "Renglón o lote de hacienda liquidado en liquidación pecuaria.",
        "properties": {
          "descripcion": {
            "type": "string",
            "description": "Categoría o tipo de hacienda (ej. NOVILLITOS, VAQUILLONAS, TOROS).",
            "example": "NOVILLITOS"
          },
          "um": {
            "type": "string",
            "description": "Unidad de medida comercial (KG o CAB).",
            "example": "KG"
          },
          "cantidad": {
            "type": "number",
            "description": "Cantidad total pesada o cabezas liquidadas.",
            "example": 12500
          },
          "precioUnitario": {
            "type": "number",
            "description": "Precio unitario pactado por unidad de medida.",
            "example": 2150
          },
          "bruto": {
            "type": "number",
            "description": "Subtotal bruto del renglón (cantidad * precioUnitario).",
            "example": 26875000
          },
          "ivaAlicuota": {
            "type": "number",
            "description": "Alícuota porcentual de IVA aplicable (ej. 10.5%).",
            "example": 10.5
          },
          "ivaImporte": {
            "type": "number",
            "description": "Importe de IVA correspondiente al renglón de hacienda.",
            "example": 2821875
          }
        },
        "required": [
          "descripcion",
          "um",
          "cantidad",
          "precioUnitario",
          "bruto",
          "ivaAlicuota",
          "ivaImporte"
        ]
      },
      "GastoLsp": {
        "type": "object",
        "description": "Concepto de gasto comercial o comisión deducido en la liquidación pecuaria.",
        "properties": {
          "concepto": {
            "type": "string",
            "description": "Descripción del gasto (ej. COMISION, FLETE, ANALISIS).",
            "example": "COMISION"
          },
          "baseImponible": {
            "type": "number",
            "description": "Base sobre la que se calculó el gasto.",
            "example": 26875000
          },
          "alicuota": {
            "type": "number",
            "description": "Porcentaje o alícuota pactada del gasto.",
            "example": 2
          },
          "importe": {
            "type": "number",
            "description": "Monto neto del gasto.",
            "example": 537500
          },
          "ivaAlicuota": {
            "type": "number",
            "description": "Alícuota de IVA aplicada al servicio comercial (ej. 21.0%).",
            "example": 21
          },
          "ivaImporte": {
            "type": "number",
            "description": "Importe de IVA liquidado sobre el gasto.",
            "example": 112875
          }
        },
        "required": [
          "concepto",
          "baseImponible",
          "alicuota",
          "importe",
          "ivaAlicuota",
          "ivaImporte"
        ]
      },
      "TributoLsp": {
        "type": "object",
        "description": "Percepción tributaria o retención impositiva en liquidación pecuaria.",
        "properties": {
          "concepto": {
            "type": "string",
            "description": "Descripción del tributo (ej. PERCEPCION INGRESOS BRUTOS, RETENCION GANANCIAS).",
            "example": "PERCEPCION INGRESOS BRUTOS"
          },
          "baseImponible": {
            "type": "number",
            "description": "Base imponible sujeta a percepción o retención.",
            "example": 26875000
          },
          "alicuota": {
            "type": "number",
            "description": "Porcentaje de la percepción/retención.",
            "example": 1.5
          },
          "importe": {
            "type": "number",
            "description": "Importe liquidado del tributo.",
            "example": 403125
          }
        },
        "required": [
          "concepto",
          "baseImponible",
          "alicuota",
          "importe"
        ]
      },
      "ComprobanteLsp": {
        "type": "object",
        "description": "Liquidación de Sector Pecuario (LSP) completa parseada desde el PDF oficial de ARCA.",
        "properties": {
          "fecha": {
            "type": "string",
            "format": "date",
            "description": "Fecha de emisión (YYYY-MM-DD).",
            "example": "2026-08-15"
          },
          "tipoAfip": {
            "type": "string",
            "description": "Código AFIP de comprobante pecuario (ej. 180=Liquidación Sector Pecuario A).",
            "example": "180"
          },
          "tipoNombre": {
            "type": "string",
            "description": "Denominación oficial del comprobante.",
            "example": "LIQUIDACION SECTOR PECUARIO A"
          },
          "puntoVenta": {
            "type": "string",
            "description": "Punto de venta emisor.",
            "example": "1"
          },
          "numero": {
            "type": "string",
            "description": "Número secuencial del comprobante.",
            "example": "452"
          },
          "numeroCompleto": {
            "type": "string",
            "description": "Formato completo PV-Número con ceros a la izquierda.",
            "example": "00001-00000452"
          },
          "cae": {
            "type": "string",
            "description": "Código de Autorización Electrónico otorgado por ARCA.",
            "example": "74321987654321"
          },
          "vtoCae": {
            "type": "string",
            "format": "date",
            "description": "Fecha de vencimiento del CAE (YYYY-MM-DD).",
            "example": "2026-08-25"
          },
          "emisorCuit": {
            "type": "string",
            "description": "CUIT del operador emisor de la liquidación.",
            "example": "30112233445"
          },
          "receptorCuit": {
            "type": "string",
            "description": "CUIT del productor pecuario destinatario.",
            "example": "20334455667"
          },
          "receptorNombre": {
            "type": "string",
            "description": "Nombre o razón social del productor pecuario.",
            "example": "AGROPECUARIA EL OMBU S.A."
          },
          "moneda": {
            "type": "string",
            "description": "Código de moneda (ej. PES).",
            "example": "PES"
          },
          "tipoCambio": {
            "type": "number",
            "description": "Tipo de cambio fijado.",
            "example": 1
          },
          "netoGravado": {
            "type": "number",
            "description": "Total neto gravado por la hacienda vendida.",
            "example": 26875000
          },
          "totalGastos": {
            "type": "number",
            "description": "Sumatoria neta de gastos y comisiones deducidas.",
            "example": 537500
          },
          "totalIva": {
            "type": "number",
            "description": "Total de IVA liquidado (hacienda + gastos).",
            "example": 2934750
          },
          "totalTributos": {
            "type": "number",
            "description": "Total de retenciones y percepciones impositivas.",
            "example": 403125
          },
          "total": {
            "type": "number",
            "description": "Importe final total de la liquidación.",
            "example": 29675375
          },
          "items": {
            "type": "array",
            "description": "Lotes y renglones de hacienda liquidados.",
            "items": {
              "$ref": "#/components/schemas/ItemHaciendaLsp"
            }
          },
          "gastos": {
            "type": "array",
            "description": "Comisiones y gastos comerciales discriminados.",
            "items": {
              "$ref": "#/components/schemas/GastoLsp"
            }
          },
          "tributos": {
            "type": "array",
            "description": "Percepciones y retenciones impositivas aplicadas.",
            "items": {
              "$ref": "#/components/schemas/TributoLsp"
            }
          }
        },
        "required": [
          "fecha",
          "tipoAfip",
          "tipoNombre",
          "puntoVenta",
          "numero",
          "numeroCompleto",
          "cae",
          "vtoCae",
          "emisorCuit",
          "receptorCuit",
          "receptorNombre",
          "moneda",
          "tipoCambio",
          "netoGravado",
          "totalGastos",
          "totalIva",
          "totalTributos",
          "total",
          "items",
          "gastos",
          "tributos"
        ]
      },
      "CacheArtefacto": {
        "type": "object",
        "description": "Marca de artefacto servido desde el cache. La respuesta declara que el dato NO vino del portal en esta ejecución y qué antigüedad tiene, para que un consumidor que recibe datos cacheados (hasta el TTL vigente del cache) pueda decidir qué hacer con ellos. Sólo se sirven artefactos completos: un servicio `fallido`, `parcial` o `sin_datos` nunca se sirve del cache. La búsqueda se indexa por (CUIT representado, servicio, ventana): el artefacto pertenece al contribuyente, no al tenant, y pedirlo exige igual una credencial propia de ese CUIT.",
        "properties": {
          "origen": {
            "type": "string",
            "enum": [
              "cache"
            ],
            "description": "Siempre `cache`, el artefacto se sirvió del cache y no del portal.",
            "example": "cache"
          },
          "edad_ms": {
            "type": "integer",
            "description": "Antigüedad del artefacto servido, en milisegundos, al momento de servirlo. Es siempre menor que el TTL del cache vigente en ese momento.",
            "example": 312000
          },
          "extraido_en": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp ISO 8601 en que se completó la extracción ORIGINAL del artefacto. Reusar un artefacto no rejuvenece este sello.",
            "example": "2026-10-22T19:24:03.000Z"
          }
        },
        "required": [
          "origen",
          "edad_ms",
          "extraido_en"
        ]
      },
      "ServicioPublico": {
        "type": "object",
        "description": "Resultado y estado de un servicio consultado en el marco del job.",
        "properties": {
          "servicio": {
            "type": "string",
            "description": "Nombre del servicio (ej. mc_recibidos, mc_emitidos, lsp_liquidaciones).",
            "example": "mc_recibidos"
          },
          "estado": {
            "$ref": "#/components/schemas/ServicioEstado"
          },
          "motivo": {
            "$ref": "#/components/schemas/MotivoError"
          },
          "conteo": {
            "$ref": "#/components/schemas/Conteo"
          },
          "filas": {
            "type": "array",
            "description": "Listado de comprobantes normalizados (disponible únicamente cuando estado es completado).",
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/ComprobanteMc"
                },
                {
                  "$ref": "#/components/schemas/ComprobanteLsp"
                }
              ]
            }
          },
          "cache": {
            "$ref": "#/components/schemas/CacheArtefacto"
          }
        },
        "required": [
          "servicio",
          "estado",
          "conteo"
        ]
      },
      "JobResponse": {
        "type": "object",
        "description": "Estado consolidado, trazabilidad de login y resultados del job.",
        "properties": {
          "job_id": {
            "type": "string",
            "description": "Identificador único del job.",
            "example": "job_a1b2c3d4e5f60718"
          },
          "estado": {
            "$ref": "#/components/schemas/JobEstado"
          },
          "creado_en": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp ISO 8601 de creación del job.",
            "example": "2026-09-17T15:40:02.000Z"
          },
          "actualizado_en": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp ISO 8601 de última actualización de estado.",
            "example": "2026-09-17T15:42:11.000Z"
          },
          "login": {
            "type": "object",
            "description": "Auditoría de sesión y transmisión de credenciales.",
            "properties": {
              "estado": {
                "$ref": "#/components/schemas/LoginEstado"
              },
              "transmisiones_de_clave": {
                "type": "integer",
                "description": "Número exacto de veces que la clave fiscal fue transmitida al portal ARCA (estrictamente 1 en ejecuciones exitosas).",
                "example": 1
              }
            },
            "required": [
              "estado",
              "transmisiones_de_clave"
            ]
          },
          "servicios": {
            "type": "array",
            "description": "Lista de servicios consultados y sus respectivos estados y filas: un elemento por servicio pedido, en el orden del pedido, cada uno con su `estado`, su `motivo` tipado, su `conteo{declarado,obtenido}` y sus `filas` (sólo si su guardia de conteo pasó). Con resultados mixtos (alguno ok y alguno `fallido`) el job agrega a `parcial` sin borrar las filas de los servicios buenos. Las ventanas internas por servicio nunca se sirven. Un servicio que se completó con un artefacto reciente trae además `cache{origen,edad_ms,extraido_en}` y, en ese caso, `conteo` es el del artefacto servido (no cero) y `login.transmisiones_de_clave` no cuenta una transmisión: el job no fue al portal.",
            "items": {
              "$ref": "#/components/schemas/ServicioPublico"
            }
          },
          "reintento_de": {
            "type": "string",
            "description": "`job_id` original que este job reintenta. Ausente en los jobs que no nacieron de un retry.",
            "example": "job_a1b2c3d4e5f60718"
          }
        },
        "required": [
          "job_id",
          "estado",
          "creado_en",
          "actualizado_en",
          "login",
          "servicios"
        ]
      },
      "PuntoVenta": {
        "type": "object",
        "description": "Punto de venta WSFE habilitado (campos whitelisteados desde el SDK).",
        "properties": {
          "numero": {
            "type": "integer",
            "description": "Número de punto de venta (Nro AFIP).",
            "example": 4
          },
          "tipo_emision": {
            "type": "string",
            "description": "Tipo de emisión (EmisionTipo AFIP).",
            "example": "CAE"
          },
          "bloqueado": {
            "type": "string",
            "description": "Marca de bloqueo AFIP (Bloqueado).",
            "example": "N"
          },
          "fecha_baja": {
            "type": "string",
            "description": "Fecha de baja (FchBaja AFIP); ausente si el punto sigue habilitado.",
            "example": "20200101"
          }
        },
        "required": [
          "numero",
          "tipo_emision",
          "bloqueado"
        ]
      },
      "PuntosVentaResponse": {
        "type": "object",
        "description": "Puntos de venta WSFE habilitados en homologación.",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wsfe"
          },
          "ambiente": {
            "type": "string",
            "example": "homologacion"
          },
          "puntos_venta": {
            "type": "array",
            "description": "Lista de puntos de venta (vacía con `aviso` cuando AFIP no informa ninguno).",
            "items": {
              "$ref": "#/components/schemas/PuntoVenta"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida (homologación no informa puntos); ausente en un éxito no vacío.",
            "example": "respuesta valida sin puntos de venta informados: homologacion no informa puntos de venta para este contribuyente"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "puntos_venta"
        ]
      },
      "UltimoComprobanteResponse": {
        "type": "object",
        "description": "Último comprobante WSFE autorizado en homologación (0 cuando el punto de venta está limpio).",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wsfe"
          },
          "ambiente": {
            "type": "string",
            "example": "homologacion"
          },
          "punto_venta": {
            "type": "integer",
            "description": "Punto de venta consultado (eco del parámetro).",
            "example": 2
          },
          "tipo_comprobante": {
            "type": "integer",
            "description": "Tipo de comprobante consultado (eco del parámetro).",
            "example": 1
          },
          "ultimo_comprobante": {
            "type": "integer",
            "description": "Último número autorizado (CbteNro AFIP); 0 cuando nunca se emitió ninguno.",
            "example": 7
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "punto_venta",
          "tipo_comprobante",
          "ultimo_comprobante"
        ]
      },
      "ComprobanteInfo": {
        "type": "object",
        "description": "Detalle de comprobante WSFE (campos whitelisteados desde el SDK; identidad de reconciliación más montos, fechas y notas para comparar un candidato contra el intento propio; nunca el payload crudo).",
        "properties": {
          "cae": {
            "type": "string",
            "description": "CAE (CodAutorizacion AFIP); ausente si el proveedor no lo informa.",
            "example": "99999999999999"
          },
          "resultado": {
            "type": "string",
            "description": "Resultado AFIP (Resultado); ausente si el proveedor no lo informa.",
            "example": "A"
          },
          "concepto": {
            "type": "integer",
            "description": "Concepto AFIP; ausente si el proveedor no lo informa.",
            "example": 1
          },
          "doc_tipo": {
            "type": "integer",
            "description": "Tipo de documento AFIP; ausente si el proveedor no lo informa.",
            "example": 99
          },
          "doc_nro": {
            "type": "integer",
            "description": "Número de documento AFIP; ausente si el proveedor no lo informa.",
            "example": 0
          },
          "cbte_desde": {
            "type": "integer",
            "description": "Número inicial del comprobante (identidad eco AFIP).",
            "example": 7
          },
          "cbte_hasta": {
            "type": "integer",
            "description": "Número final del comprobante (identidad eco AFIP).",
            "example": 7
          },
          "cbte_fch": {
            "type": "string",
            "description": "Fecha del comprobante; ausente si el proveedor no la informa.",
            "example": "20260801"
          },
          "imp_total": {
            "type": "number",
            "description": "Importe total; ausente si el proveedor no lo informa.",
            "example": 121
          },
          "imp_tot_conc": {
            "type": "number",
            "description": "Importe total de conceptos no gravados; ausente si el proveedor no lo informa.",
            "example": 0
          },
          "imp_neto": {
            "type": "number",
            "description": "Importe neto gravado; ausente si el proveedor no lo informa.",
            "example": 100
          },
          "imp_op_ex": {
            "type": "number",
            "description": "Importe de operaciones exentas; ausente si el proveedor no lo informa.",
            "example": 0
          },
          "imp_iva": {
            "type": "number",
            "description": "Importe total de IVA; ausente si el proveedor no lo informa.",
            "example": 21
          },
          "imp_trib": {
            "type": "number",
            "description": "Importe de otros tributos; ausente si el proveedor no lo informa.",
            "example": 0
          },
          "mon_id": {
            "type": "string",
            "description": "Moneda AFIP; ausente si el proveedor no la informa.",
            "example": "PES"
          },
          "mon_cotiz": {
            "type": "number",
            "description": "Cotización de la moneda; ausente si el proveedor no la informa.",
            "example": 1
          },
          "fch_vto": {
            "type": "string",
            "description": "Vencimiento del CAE (FchVto AFIP); ausente si el proveedor no lo informa.",
            "example": "20260811"
          },
          "fch_proceso": {
            "type": "string",
            "description": "Fecha de proceso AFIP; ausente si el proveedor no la informa.",
            "example": "20260801235959"
          },
          "emision_tipo": {
            "type": "string",
            "description": "Tipo de emisión AFIP (EmisionTipo); ausente si el proveedor no lo informa.",
            "example": "CAE"
          },
          "observaciones": {
            "type": "string",
            "description": "Primera observación AFIP; ausente si el proveedor no la informa.",
            "example": "aprobado"
          }
        },
        "required": [
          "cbte_desde",
          "cbte_hasta"
        ]
      },
      "ComprobanteInfoResponse": {
        "type": "object",
        "description": "Comprobante WSFE consultado en homologación.",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wsfe"
          },
          "ambiente": {
            "type": "string",
            "example": "homologacion"
          },
          "punto_venta": {
            "type": "integer",
            "description": "Punto de venta consultado (eco del parámetro).",
            "example": 2
          },
          "tipo_comprobante": {
            "type": "integer",
            "description": "Tipo de comprobante consultado (eco del parámetro).",
            "example": 1
          },
          "numero": {
            "type": "integer",
            "description": "Número de comprobante consultado (eco del path).",
            "example": 7
          },
          "comprobante": {
            "$ref": "#/components/schemas/ComprobanteInfo"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "punto_venta",
          "tipo_comprobante",
          "numero",
          "comprobante"
        ]
      },
      "ContribuyenteA4": {
        "type": "object",
        "description": "Contribuyente del padrón alcance 4 (campos whitelisteados desde el SDK).",
        "properties": {
          "id_persona": {
            "type": "integer",
            "description": "Identificador de persona AFIP (IdPersona).",
            "example": 20123456786
          },
          "tipo_persona": {
            "type": "string",
            "description": "Tipo de persona AFIP (TipoPersona); ausente si el proveedor no lo informa.",
            "example": "FISICA"
          },
          "estado_clave": {
            "type": "string",
            "description": "Estado de la clave AFIP (EstadoClave); ausente si el proveedor no lo informa.",
            "example": "ACTIVA"
          },
          "domicilio": {
            "type": "string",
            "description": "Domicilio fiscal (Domicilio AFIP); ausente si el proveedor no lo informa.",
            "example": "CALLE FALSA 123"
          }
        },
        "required": [
          "id_persona"
        ]
      },
      "PadronA4Response": {
        "type": "object",
        "description": "Contribuyente del padrón alcance 4 en homologación.",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "padron-a4"
          },
          "ambiente": {
            "type": "string",
            "example": "homologacion"
          },
          "cuit": {
            "type": "string",
            "description": "CUIT consultado (11 dígitos validados).",
            "example": "20123456786"
          },
          "contribuyente": {
            "$ref": "#/components/schemas/ContribuyenteA4"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "cuit",
          "contribuyente"
        ]
      },
      "FilaWslsp": {
        "type": "object",
        "description": "Fila de referencia WSLSP (CodigoDescripcion AFIP, campos whitelisteados).",
        "properties": {
          "codigo": {
            "type": "string",
            "description": "Código AFIP de la fila.",
            "example": "01"
          },
          "descripcion": {
            "type": "string",
            "description": "Descripción AFIP de la fila.",
            "example": "Buenos Aires"
          }
        },
        "required": [
          "codigo",
          "descripcion"
        ]
      },
      "WslspProvinciasResponse": {
        "type": "object",
        "description": "Provincias WSLSP en homologación.",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wslsp"
          },
          "ambiente": {
            "type": "string",
            "example": "homologacion"
          },
          "provincias": {
            "type": "array",
            "description": "Provincias según AFIP (vacía con `aviso` cuando la respuesta válida no trae filas).",
            "items": {
              "$ref": "#/components/schemas/FilaWslsp"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin filas: homologacion no informa provincias para este contribuyente"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "provincias"
        ]
      },
      "WslspPuntosVentaResponse": {
        "type": "object",
        "description": "Puntos de venta WSLSP en homologación.",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wslsp"
          },
          "ambiente": {
            "type": "string",
            "example": "homologacion"
          },
          "puntos_venta": {
            "type": "array",
            "description": "Puntos de venta según AFIP (vacía con `aviso`: homologación no valida la existencia de puntos de venta).",
            "items": {
              "$ref": "#/components/schemas/FilaWslsp"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin puntos de venta informados: homologacion no valida la existencia de puntos de venta (manual ARCA)"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "puntos_venta"
        ]
      },
      "WslspTiposComprobanteResponse": {
        "type": "object",
        "description": "Tipos de comprobante WSLSP en homologación.",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wslsp"
          },
          "ambiente": {
            "type": "string",
            "example": "homologacion"
          },
          "tipos_comprobante": {
            "type": "array",
            "description": "Tipos de comprobante según AFIP (vacía con `aviso` cuando la respuesta válida no trae filas).",
            "items": {
              "$ref": "#/components/schemas/FilaWslsp"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin filas: homologacion no informa tipos de comprobante para este contribuyente"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "tipos_comprobante"
        ]
      },
      "FilaWslpg": {
        "type": "object",
        "description": "Fila de referencia WSLPG (CodigoDescripcion AFIP, campos whitelisteados).",
        "properties": {
          "codigo": {
            "type": "string",
            "description": "Código AFIP de la fila.",
            "example": "2627"
          },
          "descripcion": {
            "type": "string",
            "description": "Descripción AFIP de la fila.",
            "example": "2026/2027"
          }
        },
        "required": [
          "codigo",
          "descripcion"
        ]
      },
      "WslpgCampaniasResponse": {
        "type": "object",
        "description": "Campañas WSLPG en homologación.",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wslpg"
          },
          "ambiente": {
            "type": "string",
            "example": "homologacion"
          },
          "campanias": {
            "type": "array",
            "description": "Campañas según AFIP (vacía con `aviso` cuando la respuesta válida no trae filas).",
            "items": {
              "$ref": "#/components/schemas/FilaWslpg"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin filas: homologacion no informa campanias para esta consulta (error ARCA 600: sin datos)"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "campanias"
        ]
      },
      "WslpgTiposGranoResponse": {
        "type": "object",
        "description": "Tipos de grano WSLPG en homologación.",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wslpg"
          },
          "ambiente": {
            "type": "string",
            "example": "homologacion"
          },
          "tipos_grano": {
            "type": "array",
            "description": "Tipos de grano según AFIP (vacía con `aviso` cuando la respuesta válida no trae filas).",
            "items": {
              "$ref": "#/components/schemas/FilaWslpg"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin filas: homologacion no informa tipos de grano para esta consulta (error ARCA 600: sin datos)"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "tipos_grano"
        ]
      },
      "WslpgGradosReferenciaResponse": {
        "type": "object",
        "description": "Grados de referencia WSLPG en homologación.",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wslpg"
          },
          "ambiente": {
            "type": "string",
            "example": "homologacion"
          },
          "grados_referencia": {
            "type": "array",
            "description": "Grados de referencia según AFIP (vacía con `aviso` cuando la respuesta válida no trae filas).",
            "items": {
              "$ref": "#/components/schemas/FilaWslpg"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin filas: homologacion no informa grados de referencia para esta consulta (error ARCA 600: sin datos)"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "grados_referencia"
        ]
      },
      "FilaWscpe": {
        "type": "object",
        "description": "Fila de referencia WSCPE (CodigoDescripcion AFIP, campos whitelisteados).",
        "properties": {
          "codigo": {
            "type": "string",
            "description": "Código AFIP de la fila.",
            "example": "1"
          },
          "descripcion": {
            "type": "string",
            "description": "Descripción AFIP de la fila.",
            "example": "Buenos Aires"
          }
        },
        "required": [
          "codigo",
          "descripcion"
        ]
      },
      "WscpeProvinciasResponse": {
        "type": "object",
        "description": "Provincias WSCPE en homologación.",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wscpe"
          },
          "ambiente": {
            "type": "string",
            "example": "homologacion"
          },
          "provincias": {
            "type": "array",
            "description": "Provincias según AFIP (vacía con `aviso` cuando la respuesta válida no trae filas).",
            "items": {
              "$ref": "#/components/schemas/FilaWscpe"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin filas: homologacion no informa provincias para este contribuyente"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "provincias"
        ]
      },
      "WscpeTiposGranoResponse": {
        "type": "object",
        "description": "Tipos de grano WSCPE en homologación.",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wscpe"
          },
          "ambiente": {
            "type": "string",
            "example": "homologacion"
          },
          "tipos_grano": {
            "type": "array",
            "description": "Tipos de grano según AFIP (vacía con `aviso` cuando la respuesta válida no trae filas).",
            "items": {
              "$ref": "#/components/schemas/FilaWscpe"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin filas: homologacion no informa tipos de grano para este contribuyente"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "tipos_grano"
        ]
      },
      "EmitirVoucherRequest": {
        "type": "object",
        "description": "Datos del comprobante a emitir en WSFE.",
        "properties": {
          "cuit": {
            "type": "string",
            "example": "20248130653"
          },
          "punto_venta": {
            "type": "integer",
            "minimum": 1,
            "example": 1
          },
          "tipo_comprobante": {
            "type": "integer",
            "minimum": 1,
            "example": 6
          },
          "concepto": {
            "type": "integer",
            "minimum": 1,
            "example": 1
          },
          "doc_tipo": {
            "type": "integer",
            "minimum": 0,
            "example": 99
          },
          "doc_nro": {
            "type": "integer",
            "minimum": 0,
            "example": 0
          },
          "cbte_fch": {
            "type": "string",
            "pattern": "^\\d{8}$",
            "example": "20260918"
          },
          "imp_total": {
            "type": "number",
            "minimum": 0,
            "example": 121
          },
          "imp_tot_conc": {
            "type": "number",
            "minimum": 0,
            "example": 0
          },
          "imp_neto": {
            "type": "number",
            "minimum": 0,
            "example": 100
          },
          "imp_op_ex": {
            "type": "number",
            "minimum": 0,
            "example": 0
          },
          "imp_iva": {
            "type": "number",
            "minimum": 0,
            "example": 21
          },
          "imp_trib": {
            "type": "number",
            "minimum": 0,
            "example": 0
          },
          "mon_id": {
            "type": "string",
            "example": "PES"
          },
          "mon_cotiz": {
            "type": "number",
            "example": 1
          },
          "condicion_iva_receptor_id": {
            "type": "integer",
            "example": 5
          },
          "iva": {
            "type": "array",
            "description": "Desglose discriminado de alícuotas de IVA (opcional).",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "integer",
                  "description": "Código AFIP de alícuota."
                },
                "base_imp": {
                  "type": "number",
                  "description": "Base imponible de la alícuota."
                },
                "importe": {
                  "type": "number",
                  "description": "Importe de IVA liquidado para la alícuota."
                }
              }
            }
          },
          "tributos": {
            "type": "array",
            "description": "Percepciones y otros tributos (opcional; AFIP `Tributos`). Cada fila se mapea a `{ id, desc, base_imp, alic, importe }`.",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "integer",
                  "description": "Código AFIP del tributo."
                },
                "desc": {
                  "type": "string",
                  "description": "Descripción del tributo."
                },
                "base_imp": {
                  "type": "number",
                  "description": "Base imponible del tributo."
                },
                "alic": {
                  "type": "number",
                  "description": "Alícuota del tributo (porcentaje)."
                },
                "importe": {
                  "type": "number",
                  "description": "Importe liquidado del tributo."
                }
              }
            }
          },
          "cbtes_asoc": {
            "type": "array",
            "description": "Comprobantes asociados para Notas de Crédito/Débito (opcional; AFIP `CbtesAsoc`).",
            "items": {
              "type": "object",
              "properties": {
                "tipo": {
                  "type": "integer",
                  "description": "Tipo de comprobante asociado."
                },
                "pto_vta": {
                  "type": "integer",
                  "description": "Punto de venta del comprobante asociado."
                },
                "nro": {
                  "type": "integer",
                  "description": "Número del comprobante asociado."
                },
                "cuit": {
                  "type": "string",
                  "description": "CUIT del emisor del comprobante asociado."
                },
                "cbte_fch": {
                  "type": "string",
                  "pattern": "^\\d{8}$",
                  "description": "Fecha del comprobante asociado (AAAAMMDD); opcional."
                }
              }
            }
          },
          "periodo_asoc": {
            "type": "object",
            "description": "Período asociado para comprobantes de concepto servicios (opcional; AFIP `PeriodoAsoc`).",
            "properties": {
              "fch_desde": {
                "type": "string",
                "description": "Fecha desde del período asociado (AAAAMMDD)."
              },
              "fch_hasta": {
                "type": "string",
                "description": "Fecha hasta del período asociado (AAAAMMDD)."
              }
            }
          },
          "generar_pdf": {
            "type": "boolean",
            "description": "Indica si debe generarse y adjuntarse el PDF oficial de la factura en la respuesta (default: false).",
            "example": true
          },
          "emisor": {
            "type": "object",
            "description": "Datos complementarios del emisor para la confección del PDF oficial.",
            "properties": {
              "razon_social": {
                "type": "string"
              },
              "domicilio": {
                "type": "string"
              },
              "condicion_iva": {
                "type": "string"
              },
              "ingresos_brutos": {
                "type": "string"
              },
              "inicio_actividades": {
                "type": "string"
              }
            }
          },
          "receptor": {
            "type": "object",
            "description": "Datos complementarios del receptor para la confección del PDF oficial.",
            "properties": {
              "razon_social": {
                "type": "string"
              },
              "domicilio": {
                "type": "string"
              },
              "condicion_iva": {
                "type": "string"
              },
              "condicion_venta": {
                "type": "string"
              }
            }
          },
          "items": {
            "type": "array",
            "description": "Detalle de ítems facturados para el PDF oficial.",
            "items": {
              "type": "object",
              "properties": {
                "codigo": {
                  "type": "string"
                },
                "descripcion": {
                  "type": "string"
                },
                "cantidad": {
                  "type": "number"
                },
                "unidad_medida": {
                  "type": "string"
                },
                "precio_unitario": {
                  "type": "number"
                },
                "subtotal": {
                  "type": "number"
                },
                "alicuota_iva": {
                  "type": "number"
                }
              }
            }
          },
          "credencial_emisora": {
            "type": "object",
            "description": "Sobre híbrido opcional que identifica al emisor del comprobante (emisión multi-empresa). Presente ⇒ el emisor es el de la credencial; ausente ⇒ vale el emisor configurado por el servidor. Un sobre presente y roto es `400 credencial_invalida` (nunca fallback silencioso).",
            "properties": {
              "esquema": {
                "type": "string",
                "description": "Identificador de versión del sobre; viaja como AAD del GCM.",
                "example": "rsa-oaep-sha256+aes-256-gcm"
              },
              "clave": {
                "type": "string",
                "description": "base64 de RSA-OAEP-SHA256(clave AES de 32 bytes)."
              },
              "iv": {
                "type": "string",
                "description": "base64 del IV GCM de 12 bytes."
              },
              "tag": {
                "type": "string",
                "description": "base64 del tag de autenticación GCM de 16 bytes."
              },
              "datos": {
                "type": "string",
                "description": "base64 del ciphertext AES-256-GCM del JSON `{ cuit, cert, key }`."
              }
            }
          }
        },
        "required": [
          "cuit",
          "punto_venta",
          "tipo_comprobante",
          "concepto",
          "doc_tipo",
          "doc_nro",
          "cbte_fch",
          "imp_total",
          "imp_neto",
          "imp_iva"
        ]
      },
      "EmitirVoucherResponse": {
        "type": "object",
        "description": "Resultado de la emisión o replay de comprobante WSFE.",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wsfe"
          },
          "ambiente": {
            "type": "string",
            "example": "homologacion"
          },
          "idempotency_key": {
            "type": "string",
            "example": "orden-12345"
          },
          "numero": {
            "type": "integer",
            "example": 11
          },
          "cae": {
            "type": "string",
            "example": "74012345678901"
          },
          "cae_fch_vto": {
            "type": "string",
            "example": "20260928"
          },
          "replay": {
            "type": "boolean",
            "example": false
          },
          "pdf_base64": {
            "type": "string",
            "description": "Comprobante oficial en formato PDF codificado en Base64 con QR fiscal.",
            "example": "JVBERi0xLjQK..."
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "idempotency_key",
          "numero",
          "cae",
          "replay"
        ]
      },
      "WsMotivoError": {
        "type": "string",
        "enum": [
          "servicio_no_configurado",
          "ws_no_disponible",
          "not_found",
          "parametros_invalidos",
          "credencial_invalida",
          "credencial_no_soportada",
          "idempotency_key_requerida",
          "idempotency_conflict_payload",
          "idempotency_in_flight",
          "intento_rechazado",
          "ws_incierto_verificar",
          "emision_rechazada_afip"
        ],
        "description": "Taxonomía cerrada de errores de la superficie WS (consultas y emisión), espejo de los códigos que emiten los handlers de emisión y de configuración WS. - servicio_no_configurado: el servidor no tiene conexión/emisor WS inyectado (503). - ws_no_disponible: el proveedor WSFE/padrón/WSLSP/WSLPG/WSCPE/AFIP falló o respondió con error; el detalle se sanitiza (502). - not_found: el proveedor informó ausencia limpia, en padrón A4 o en consulta de comprobante WSFE (404). - parametros_invalidos: entrada malformada (query/path/cuerpo) antes de invocar al proveedor (400). - credencial_invalida: el sobre `credencial_emisora` está presente pero es inservible (400). - credencial_no_soportada: el despliegue no tiene cableado el carril por credencial (400). - idempotency_key_requerida: falta el header `Idempotency-Key` (400). - idempotency_conflict_payload: la misma clave de idempotencia se reutilizó con otro payload (409). - idempotency_in_flight: la misma clave de idempotencia tiene una emisión en curso (409). - intento_rechazado: la clave de idempotencia ya fue rechazada (422). - ws_incierto_verificar: timeout con estado incierto registrado; hay que verificar antes de reintentar (502). - emision_rechazada_afip: AFIP rechazó formalmente el comprobante (422)."
      },
      "WsErrorResponse": {
        "type": "object",
        "properties": {
          "motivo": {
            "$ref": "#/components/schemas/WsMotivoError"
          }
        },
        "required": [
          "motivo"
        ]
      },
      "CatalogoParametroFila": {
        "type": "object",
        "description": "Fila de un catálogo de parámetros WSFE con campos whitelisteados: `id` <- AFIP `Id`, `descripcion` <- AFIP `Desc`, `fch_desde` <- AFIP `FchDesde`, `fch_hasta` <- AFIP `FchHasta`. No se agregan campos que AFIP no devuelva.",
        "properties": {
          "id": {
            "type": [
              "integer",
              "string"
            ],
            "description": "Código AFIP de la fila. Se conserva tal como lo entrega el proveedor porque es una identidad y no una cantidad: algunos sets son numéricos y otros alfanuméricos, y un código con ceros a la izquierda no se coacciona a número.",
            "example": 1
          },
          "descripcion": {
            "type": "string",
            "description": "Descripción AFIP de la fila (Desc).",
            "example": "Factura A"
          },
          "fch_desde": {
            "type": "string",
            "description": "Vigencia desde (FchDesde AFIP); cadena vacía cuando AFIP no la informa.",
            "example": "20000101"
          },
          "fch_hasta": {
            "type": "string",
            "description": "Vigencia hasta (FchHasta AFIP); cadena vacía cuando AFIP no la informa.",
            "example": ""
          }
        },
        "required": [
          "id",
          "descripcion",
          "fch_desde",
          "fch_hasta"
        ]
      },
      "CatalogoIvaReceptorFila": {
        "type": "object",
        "description": "Fila de FEParamGetCondicionIvaReceptor (campos whitelisteados): `id` <- AFIP `Id`, `descripcion` <- AFIP `Desc`, `cmp_clase` <- AFIP `Cmp_Clase`.",
        "properties": {
          "id": {
            "type": [
              "integer",
              "string"
            ],
            "description": "Código AFIP de la condición de IVA del receptor.",
            "example": 5
          },
          "descripcion": {
            "type": "string",
            "description": "Descripción AFIP de la condición (Desc).",
            "example": "Responsable Inscripto"
          },
          "cmp_clase": {
            "type": "string",
            "description": "Clase de comprobante a la que aplica la condición (Cmp_Clase AFIP); texto tal como lo informa AFIP.",
            "example": "A"
          }
        },
        "required": [
          "id",
          "descripcion",
          "cmp_clase"
        ]
      },
      "CatalogoPaisFila": {
        "type": "object",
        "description": "Fila de FEParamGetTiposPaises (campos whitelisteados): `id` <- AFIP `Id`, `descripcion` <- AFIP `Desc`.",
        "properties": {
          "id": {
            "type": [
              "integer",
              "string"
            ],
            "description": "Código AFIP del país.",
            "example": 200
          },
          "descripcion": {
            "type": "string",
            "description": "Nombre AFIP del país (Desc).",
            "example": "Argentina"
          }
        },
        "required": [
          "id",
          "descripcion"
        ]
      },
      "CatalogoActividadFila": {
        "type": "object",
        "description": "Fila de FEParamGetActividades (campos whitelisteados): `id` <- AFIP `Id`, `orden` <- AFIP `Orden`, `descripcion` <- AFIP `Desc`.",
        "properties": {
          "id": {
            "type": [
              "integer",
              "string"
            ],
            "description": "Código AFIP de la actividad.",
            "example": 620100
          },
          "orden": {
            "type": "integer",
            "description": "Orden AFIP de la fila (Orden); por ser una cantidad, una cadena numérica del proveedor se normaliza a entero.",
            "example": 1
          },
          "descripcion": {
            "type": "string",
            "description": "Descripción AFIP de la actividad (Desc).",
            "example": "Servicios de informatica"
          }
        },
        "required": [
          "id",
          "orden",
          "descripcion"
        ]
      },
      "WsfeTiposMonedaResponse": {
        "type": "object",
        "description": "Catálogo FEParamGetTiposMonedas del ambiente de la API Key (`homologacion` o `produccion`).",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wsfe"
          },
          "ambiente": {
            "type": "string",
            "description": "`homologacion` con una API Key de test y `produccion` con una de live, según el ambiente de la key; sin fallback cruzado entre ambientes.",
            "example": "homologacion"
          },
          "tipos_moneda": {
            "type": "array",
            "description": "Monedas según AFIP (vacía con `aviso` cuando la respuesta válida no trae filas).",
            "items": {
              "$ref": "#/components/schemas/CatalogoParametroFila"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin filas: el proveedor no informo filas para este catalogo de referencia"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "tipos_moneda"
        ]
      },
      "WsfeTiposIvaResponse": {
        "type": "object",
        "description": "Catálogo FEParamGetTiposIva del ambiente de la API Key (`homologacion` o `produccion`).",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wsfe"
          },
          "ambiente": {
            "type": "string",
            "description": "`homologacion` con una API Key de test y `produccion` con una de live, según el ambiente de la key; sin fallback cruzado entre ambientes.",
            "example": "homologacion"
          },
          "tipos_iva": {
            "type": "array",
            "description": "Alícuotas de IVA según AFIP (vacía con `aviso` cuando la respuesta válida no trae filas).",
            "items": {
              "$ref": "#/components/schemas/CatalogoParametroFila"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin filas: el proveedor no informo filas para este catalogo de referencia"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "tipos_iva"
        ]
      },
      "WsfeTiposDocResponse": {
        "type": "object",
        "description": "Catálogo FEParamGetTiposDoc del ambiente de la API Key (`homologacion` o `produccion`).",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wsfe"
          },
          "ambiente": {
            "type": "string",
            "description": "`homologacion` con una API Key de test y `produccion` con una de live, según el ambiente de la key; sin fallback cruzado entre ambientes.",
            "example": "homologacion"
          },
          "tipos_doc": {
            "type": "array",
            "description": "Tipos de documento según AFIP (vacía con `aviso` cuando la respuesta válida no trae filas).",
            "items": {
              "$ref": "#/components/schemas/CatalogoParametroFila"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin filas: el proveedor no informo filas para este catalogo de referencia"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "tipos_doc"
        ]
      },
      "WsfeTiposConceptoResponse": {
        "type": "object",
        "description": "Catálogo FEParamGetTiposConcepto del ambiente de la API Key (`homologacion` o `produccion`).",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wsfe"
          },
          "ambiente": {
            "type": "string",
            "description": "`homologacion` con una API Key de test y `produccion` con una de live, según el ambiente de la key; sin fallback cruzado entre ambientes.",
            "example": "homologacion"
          },
          "tipos_concepto": {
            "type": "array",
            "description": "Tipos de concepto según AFIP (vacía con `aviso` cuando la respuesta válida no trae filas).",
            "items": {
              "$ref": "#/components/schemas/CatalogoParametroFila"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin filas: el proveedor no informo filas para este catalogo de referencia"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "tipos_concepto"
        ]
      },
      "WsfeTiposComprobanteResponse": {
        "type": "object",
        "description": "Catálogo FEParamGetTiposCbte del ambiente de la API Key (`homologacion` o `produccion`).",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wsfe"
          },
          "ambiente": {
            "type": "string",
            "description": "`homologacion` con una API Key de test y `produccion` con una de live, según el ambiente de la key; sin fallback cruzado entre ambientes.",
            "example": "homologacion"
          },
          "tipos_comprobante": {
            "type": "array",
            "description": "Tipos de comprobante WSFE según AFIP (vacía con `aviso` cuando la respuesta válida no trae filas).",
            "items": {
              "$ref": "#/components/schemas/CatalogoParametroFila"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin filas: el proveedor no informo filas para este catalogo de referencia"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "tipos_comprobante"
        ]
      },
      "WsfeActividadesResponse": {
        "type": "object",
        "description": "Catálogo FEParamGetActividades del ambiente de la API Key (`homologacion` o `produccion`).",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wsfe"
          },
          "ambiente": {
            "type": "string",
            "description": "`homologacion` con una API Key de test y `produccion` con una de live, según el ambiente de la key; sin fallback cruzado entre ambientes.",
            "example": "homologacion"
          },
          "actividades": {
            "type": "array",
            "description": "Actividades según AFIP (vacía con `aviso` cuando la respuesta válida no trae filas).",
            "items": {
              "$ref": "#/components/schemas/CatalogoActividadFila"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin filas: el proveedor no informo filas para este catalogo de referencia"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "actividades"
        ]
      },
      "WsfePaisesResponse": {
        "type": "object",
        "description": "Catálogo FEParamGetTiposPaises del ambiente de la API Key (`homologacion` o `produccion`).",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wsfe"
          },
          "ambiente": {
            "type": "string",
            "description": "`homologacion` con una API Key de test y `produccion` con una de live, según el ambiente de la key; sin fallback cruzado entre ambientes.",
            "example": "homologacion"
          },
          "paises": {
            "type": "array",
            "description": "Países según AFIP (vacía con `aviso` cuando la respuesta válida no trae filas).",
            "items": {
              "$ref": "#/components/schemas/CatalogoPaisFila"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin filas: el proveedor no informo filas para este catalogo de referencia"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "paises"
        ]
      },
      "WsfeTiposTributoResponse": {
        "type": "object",
        "description": "Catálogo FEParamGetTiposTributos del ambiente de la API Key (`homologacion` o `produccion`).",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wsfe"
          },
          "ambiente": {
            "type": "string",
            "description": "`homologacion` con una API Key de test y `produccion` con una de live, según el ambiente de la key; sin fallback cruzado entre ambientes.",
            "example": "homologacion"
          },
          "tipos_tributo": {
            "type": "array",
            "description": "Tipos de tributo según AFIP (vacía con `aviso` cuando la respuesta válida no trae filas).",
            "items": {
              "$ref": "#/components/schemas/CatalogoParametroFila"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin filas: el proveedor no informo filas para este catalogo de referencia"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "tipos_tributo"
        ]
      },
      "WsfeTiposIvaReceptorResponse": {
        "type": "object",
        "description": "Catálogo FEParamGetCondicionIvaReceptor del ambiente de la API Key (`homologacion` o `produccion`).",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wsfe"
          },
          "ambiente": {
            "type": "string",
            "description": "`homologacion` con una API Key de test y `produccion` con una de live, según el ambiente de la key; sin fallback cruzado entre ambientes.",
            "example": "homologacion"
          },
          "tipos_iva_receptor": {
            "type": "array",
            "description": "Condiciones de IVA del receptor según AFIP, sin filtrar por `ClaseCmp` (vacía con `aviso` cuando la respuesta válida no trae filas).",
            "items": {
              "$ref": "#/components/schemas/CatalogoIvaReceptorFila"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin filas: el proveedor no informo filas para este catalogo de referencia"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "tipos_iva_receptor"
        ]
      },
      "WsfeTiposOpcionalResponse": {
        "type": "object",
        "description": "Catálogo FEParamGetTiposOpcional del ambiente de la API Key (`homologacion` o `produccion`).",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wsfe"
          },
          "ambiente": {
            "type": "string",
            "description": "`homologacion` con una API Key de test y `produccion` con una de live, según el ambiente de la key; sin fallback cruzado entre ambientes.",
            "example": "homologacion"
          },
          "tipos_opcional": {
            "type": "array",
            "description": "Tipos de dato opcional según AFIP (vacía con `aviso` cuando la respuesta válida no trae filas).",
            "items": {
              "$ref": "#/components/schemas/CatalogoParametroFila"
            }
          },
          "aviso": {
            "type": "string",
            "description": "Advisory presente SOLO en un 200 con lista vacía válida; ausente en un éxito no vacío.",
            "example": "respuesta valida sin filas: el proveedor no informo filas para este catalogo de referencia"
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "tipos_opcional"
        ]
      },
      "WsStatusResponse": {
        "type": "object",
        "description": "Estado del servidor WSFE según AFIP.",
        "properties": {
          "servicio": {
            "type": "string",
            "example": "wsfe"
          },
          "ambiente": {
            "type": "string",
            "description": "`homologacion` con una API Key de test y `produccion` con una de live, según el ambiente de la key.",
            "example": "homologacion"
          },
          "app_server": {
            "type": "string",
            "description": "AppServer informado por AFIP (AppServer del ServerStatus)."
          },
          "db_server": {
            "type": "string",
            "description": "DbServer informado por AFIP (DbServer del ServerStatus)."
          },
          "auth_server": {
            "type": "string",
            "description": "AuthServer informado por AFIP (AuthServer del ServerStatus)."
          }
        },
        "required": [
          "servicio",
          "ambiente",
          "app_server",
          "db_server",
          "auth_server"
        ]
      },
      "AdminError": {
        "type": "string",
        "enum": [
          "admin_no_configurado",
          "unauthorized",
          "parametros_invalidos",
          "tenant_existente",
          "tenant_inexistente",
          "key_existente",
          "hash_invalido",
          "no_encontrado"
        ],
        "description": "Taxonomía cerrada del plano de administración. `admin_no_configurado` (503) es el único código que puede aparecer sin credencial admin válida; el resto exige el Bearer admin.",
        "example": "parametros_invalidos"
      },
      "AdminErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "$ref": "#/components/schemas/AdminError"
          }
        },
        "required": [
          "error"
        ]
      },
      "AdminCrearTenantRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Identificador del tenant.",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "example": "acme"
          },
          "nombre": {
            "type": "string",
            "description": "Nombre legible del tenant.",
            "example": "Acme SA"
          }
        },
        "required": [
          "id",
          "nombre"
        ]
      },
      "AdminTenantResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "nombre": {
            "type": "string"
          },
          "creado_en": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "nombre",
          "creado_en"
        ]
      },
      "AdminTenantConKeys": {
        "allOf": [
          {
            "$ref": "#/components/schemas/AdminTenantResponse"
          },
          {
            "type": "object",
            "properties": {
              "activo": {
                "type": "integer",
                "description": "1 activo / 0 inactivo.",
                "example": 1
              },
              "keys": {
                "type": "integer",
                "description": "Cantidad de claves emitidas del tenant (incluidas las revocadas).",
                "example": 2
              }
            },
            "required": [
              "activo",
              "keys"
            ]
          }
        ]
      },
      "AdminTenantsListResponse": {
        "type": "object",
        "properties": {
          "tenants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AdminTenantConKeys"
            }
          }
        },
        "required": [
          "tenants"
        ]
      },
      "AdminEmitirApiKeyRequest": {
        "type": "object",
        "properties": {
          "company_id": {
            "type": "string",
            "description": "Sub-cuenta de plataforma integradora (opcional)."
          },
          "ambiente": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ],
            "description": "Ambiente de la clave (opcional). Si se omite y se provee `api_key`, se infiere del prefijo (`live_sec_` ⇒ live, resto ⇒ test); si se omite todo, se genera una `test_sec_`."
          },
          "api_key": {
            "type": "string",
            "description": "Clave a fijar explícitamente (opcional, útil en migraciones); igual sólo se guarda su hash SHA-256."
          }
        }
      },
      "AdminApiKeyIssuedResponse": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "Texto plano de la clave; se devuelve EXACTAMENTE UNA VEZ.",
            "example": "test_sec_0123456789abcdef0123456789abcdef"
          },
          "key_hash": {
            "type": "string"
          },
          "tenant_id": {
            "type": "string"
          },
          "company_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "ambiente": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "creado_en": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "key",
          "key_hash",
          "tenant_id",
          "company_id",
          "ambiente",
          "creado_en"
        ]
      },
      "AdminApiKeyMetadata": {
        "type": "object",
        "properties": {
          "key_hash": {
            "type": "string"
          },
          "tenant_id": {
            "type": "string"
          },
          "company_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "ambiente": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "activo": {
            "type": "integer"
          },
          "creado_en": {
            "type": "string",
            "format": "date-time"
          },
          "ultimo_uso_en": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "key_hash",
          "tenant_id",
          "company_id",
          "ambiente",
          "activo",
          "creado_en",
          "ultimo_uso_en"
        ]
      },
      "AdminApiKeysListResponse": {
        "type": "object",
        "properties": {
          "keys": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AdminApiKeyMetadata"
            }
          }
        },
        "required": [
          "keys"
        ]
      },
      "AdminRevokeResponse": {
        "type": "object",
        "properties": {
          "key_hash": {
            "type": "string"
          },
          "activo": {
            "type": "boolean",
            "example": false
          }
        },
        "required": [
          "key_hash",
          "activo"
        ]
      },
      "AdminMeteringResponse": {
        "type": "object",
        "properties": {
          "tenant_id": {
            "type": "string"
          },
          "company_id": {
            "type": "string",
            "description": "Presente sólo cuando se pidió el filtro."
          },
          "ambiente": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ],
            "description": "Presente sólo cuando se pidió el filtro."
          },
          "total": {
            "type": "integer",
            "description": "Total de uso registrado en `arca_metering`."
          }
        },
        "required": [
          "tenant_id",
          "total"
        ]
      },
      "SagLocalidad": {
        "type": "object",
        "properties": {
          "codLoc": {
            "type": "string",
            "description": "Código municipal/localidad en SAG",
            "example": "211"
          },
          "nombre": {
            "type": "string",
            "description": "Nombre del municipio o localidad",
            "example": "ARATA"
          }
        },
        "required": [
          "codLoc",
          "nombre"
        ]
      },
      "SagLocalidadesResponse": {
        "type": "object",
        "properties": {
          "localidades": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SagLocalidad"
            }
          }
        },
        "required": [
          "localidades"
        ]
      },
      "SagSaldoRequest": {
        "type": "object",
        "properties": {
          "cuit": {
            "type": "string",
            "description": "CUIT del contribuyente (con o sin guiones)",
            "example": "20-24813065-3"
          },
          "codLoc": {
            "type": "string",
            "description": "Código de localidad / municipalidad",
            "example": "211"
          },
          "anio": {
            "type": "integer",
            "description": "Año fiscal a consultar (por defecto año actual)",
            "example": 2026
          },
          "claveCifradaB64": {
            "type": "string",
            "description": "Clave de SAG cifrada con la clave pública RSA efímera (RSA-OAEP-SHA256)"
          },
          "clave": {
            "type": "string",
            "description": "Clave de SAG en texto plano (alternativa para entornos de desarrollo)"
          }
        },
        "required": [
          "cuit",
          "codLoc"
        ]
      },
      "SagSaldoResponse": {
        "type": "object",
        "properties": {
          "cuit": {
            "type": "string",
            "example": "20-24813065-3"
          },
          "codLoc": {
            "type": "string",
            "example": "211"
          },
          "anio": {
            "type": "integer",
            "example": 2026
          },
          "saldo": {
            "type": "number",
            "description": "Saldo numérico normalizado",
            "example": 199464.38
          },
          "saldoFormateado": {
            "type": "string",
            "description": "Saldo con formato regional argentino",
            "example": "199.464,38"
          }
        },
        "required": [
          "cuit",
          "codLoc",
          "anio",
          "saldo",
          "saldoFormateado"
        ]
      },
      "SagMovimiento": {
        "type": "object",
        "properties": {
          "fecha": {
            "type": "string",
            "description": "Fecha del movimiento (DD/MM/AAAA)",
            "example": "12/10/2026"
          },
          "concepto": {
            "type": "string",
            "description": "Descripción del concepto",
            "example": "GUIA de GANADO Nro.2468712"
          },
          "debe": {
            "type": "number",
            "example": 457619.88
          },
          "haber": {
            "type": "number",
            "example": 0
          },
          "saldo": {
            "type": "number",
            "example": 199464.38
          }
        },
        "required": [
          "fecha",
          "concepto",
          "debe",
          "haber",
          "saldo"
        ]
      },
      "SagMovimientosRequest": {
        "type": "object",
        "properties": {
          "cuit": {
            "type": "string",
            "example": "20-24813065-3"
          },
          "codLoc": {
            "type": "string",
            "example": "211"
          },
          "anio": {
            "type": "integer",
            "example": 2026
          },
          "claveCifradaB64": {
            "type": "string"
          },
          "clave": {
            "type": "string"
          }
        },
        "required": [
          "cuit",
          "codLoc"
        ]
      },
      "SagMovimientosResponse": {
        "type": "object",
        "properties": {
          "cuit": {
            "type": "string",
            "example": "20-24813065-3"
          },
          "codLoc": {
            "type": "string",
            "example": "211"
          },
          "totalMovimientos": {
            "type": "integer",
            "example": 45
          },
          "movimientos": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SagMovimiento"
            }
          }
        },
        "required": [
          "cuit",
          "codLoc",
          "totalMovimientos",
          "movimientos"
        ]
      },
      "SagItemCotizacion": {
        "type": "object",
        "properties": {
          "codEspecie": {
            "type": "integer",
            "description": "Código numérico de especie ganadera en SAG (1: NOVILLOS, etc.)",
            "example": 1
          },
          "especieDetalle": {
            "type": "string",
            "description": "Nombre descriptivo de la especie",
            "example": "NOVILLOS"
          },
          "cantidad": {
            "type": "integer",
            "description": "Cantidad de cabezas / animales",
            "example": 40
          }
        },
        "required": [
          "codEspecie",
          "especieDetalle",
          "cantidad"
        ]
      },
      "SagCotizacionRequest": {
        "type": "object",
        "properties": {
          "cuit": {
            "type": "string",
            "example": "20-24813065-3"
          },
          "codLoc": {
            "type": "string",
            "example": "211"
          },
          "destino": {
            "type": "string",
            "description": "Código de destino de la guía (ej. vf para venta faena, tl para tránsito local)",
            "example": "vf"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SagItemCotizacion"
            }
          },
          "claveCifradaB64": {
            "type": "string"
          },
          "clave": {
            "type": "string"
          }
        },
        "required": [
          "cuit",
          "codLoc",
          "destino",
          "items"
        ]
      },
      "SagCotizacionResponse": {
        "type": "object",
        "properties": {
          "nroGuiaTemporal": {
            "type": "string",
            "description": "Identificador temporal de trabajo asignado en SAG",
            "example": "1293825"
          },
          "importeGuia": {
            "type": "number",
            "description": "Tasa municipal de guía liquidada",
            "example": 457619.88
          },
          "importeIb": {
            "type": "number",
            "description": "Anticipo de Ingresos Brutos de Rentas La Pampa",
            "example": 306757.92
          },
          "total": {
            "type": "number",
            "description": "Total a abonar (guía + IIBB)",
            "example": 764377.8
          },
          "destino": {
            "type": "string",
            "example": "vf"
          },
          "codLoc": {
            "type": "string",
            "example": "211"
          }
        },
        "required": [
          "nroGuiaTemporal",
          "importeGuia",
          "importeIb",
          "total",
          "destino",
          "codLoc"
        ]
      },
      "SagErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "example": "credencial_invalida"
          },
          "codigo": {
            "type": "string",
            "example": "AUTH_FAIL"
          }
        },
        "required": [
          "error"
        ]
      },
      "SrtCredencialRequest": {
        "type": "object",
        "description": "Credencial del PORTAL ARCA del representado. SRT no tiene login propio: la sesión se abre por el SSO de ARCA y el driver toma el ticket del servicio `srt_eservicios`.",
        "properties": {
          "cuit": {
            "type": "string",
            "description": "CUIT del representado en el portal ARCA",
            "example": "20-00000000-0"
          },
          "claveCifradaB64": {
            "type": "string",
            "description": "Clave fiscal cifrada con la clave pública RSA efímera (RSA-OAEP-SHA256)"
          },
          "clave": {
            "type": "string",
            "description": "Clave fiscal en texto plano. Sólo la acepta un despliegue que la habilita explícitamente (`SRT_CLAVE_FISCAL_EN_CLARO=1`): en producción la clave en claro viaja por el cable, así que la respuesta es 400 `credencial_en_claro_no_permitida`. El camino de producción es `claveCifradaB64`."
          }
        },
        "required": [
          "cuit"
        ]
      },
      "SrtExpedientesRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/SrtCredencialRequest"
          },
          {
            "type": "object",
            "properties": {
              "numExpdte": {
                "type": "string",
                "description": "Número de expediente a filtrar: SÓLO la parte numérica, sin la barra del año. El portal responde 500 si se le pasa `418884/26`. Por defecto, todos los expedientes del representado.",
                "example": "418884"
              },
              "numAnio": {
                "type": "string",
                "description": "Año del expediente a filtrar, de CUATRO dígitos (`2026`). Con dos dígitos (`26`) el portal no filtra y devuelve cero resultados. Sin `numExpdte`, el portal ignora el año.",
                "example": "2026"
              }
            }
          }
        ]
      },
      "SrtDamnificado": {
        "type": "object",
        "properties": {
          "cuil": {
            "type": "string",
            "example": "20-22774993-9"
          },
          "nombre": {
            "type": "string",
            "example": "NOMBRE EJEMPLO"
          }
        }
      },
      "SrtExpediente": {
        "type": "object",
        "properties": {
          "oid": {
            "type": "integer",
            "description": "Identificador del expediente (clave de la extracción)",
            "example": 5972601
          },
          "nro": {
            "type": "string",
            "description": "Número de expediente",
            "example": "134503/26"
          },
          "inicio": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Fecha de inicio del expediente"
          },
          "motivo": {
            "type": [
              "string",
              "null"
            ],
            "description": "Motivo del expediente",
            "example": "Divergencia en el Alta"
          },
          "damnificado": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/SrtDamnificado"
              },
              {
                "type": "null"
              }
            ]
          },
          "generarIngreso": {
            "type": "boolean"
          },
          "imprimible": {
            "type": "boolean"
          },
          "fueVisto": {
            "type": "boolean"
          },
          "comunicacionesSinLectura": {
            "type": "integer"
          },
          "ultimaComunicacionSinLeer": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "oid",
          "nro"
        ]
      },
      "SrtExpedientesResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "data": {
            "type": "object",
            "properties": {
              "total": {
                "type": "integer"
              },
              "expedientes": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/SrtExpediente"
                }
              }
            }
          }
        }
      },
      "SrtComunicacion": {
        "type": "object",
        "description": "Una notificación del expediente.",
        "properties": {
          "traId": {
            "type": "string",
            "description": "Identificador de la notificación (con él se pide el detalle)",
            "example": "269201865"
          },
          "catId": {
            "type": "string",
            "example": "2"
          },
          "tipoActor": {
            "type": "string",
            "example": "1"
          },
          "fecha": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Fecha de la notificación (hora local del portal)"
          },
          "expediente": {
            "type": [
              "string",
              "null"
            ],
            "example": "134503/26"
          },
          "remitente": {
            "type": [
              "string",
              "null"
            ],
            "example": "SRT"
          },
          "sector": {
            "type": [
              "string",
              "null"
            ],
            "example": "Comunicaciones"
          },
          "tipoComunicacion": {
            "type": [
              "string",
              "null"
            ],
            "description": "El motivo de la notificación (su tipo de comunicación)",
            "example": "Notificación de Citación"
          },
          "ultimoEstado": {
            "type": [
              "string",
              "null"
            ],
            "example": "Leído"
          },
          "fechaUltimoEstado": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "SrtComunicacionesResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "data": {
            "type": "object",
            "properties": {
              "oid": {
                "type": "integer"
              },
              "total": {
                "type": "integer"
              },
              "comunicaciones": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/SrtComunicacion"
                }
              }
            }
          }
        }
      },
      "SrtAdjunto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Identificador del adjunto (con él se descarga el binario)",
            "example": 135128843
          },
          "nombre": {
            "type": "string",
            "example": "NotificacionCitacion.pdf"
          },
          "fecha": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "tamano": {
            "type": [
              "integer",
              "null"
            ]
          },
          "url": {
            "type": "string",
            "description": "Ruta de descarga en el portal",
            "example": "/MiVentanilla/Download.aspx?id=135128843&idTipoRef=8&nombre=NotificacionCitacion.pdf"
          }
        }
      },
      "SrtDetalleComunicacion": {
        "type": "object",
        "properties": {
          "tipo": {
            "type": [
              "string",
              "null"
            ],
            "example": "Notificación de Citación"
          },
          "fecha": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "remitente": {
            "type": [
              "string",
              "null"
            ],
            "example": "SRT"
          },
          "detalle": {
            "type": [
              "string",
              "null"
            ],
            "description": "Texto de la notificación"
          },
          "adjuntos": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SrtAdjunto"
            }
          }
        }
      },
      "SrtDetalleResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "data": {
            "$ref": "#/components/schemas/SrtDetalleComunicacion"
          }
        }
      },
      "SrtTipoIngreso": {
        "type": "object",
        "properties": {
          "valor": {
            "type": "integer",
            "example": 533
          },
          "nombre": {
            "type": "string",
            "example": "Requerimiento de la C.M.J."
          }
        }
      },
      "SrtCatalogosResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "data": {
            "type": "object",
            "properties": {
              "tiposIngreso": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/SrtTipoIngreso"
                }
              }
            }
          }
        }
      },
      "SrtErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "example": "credencial_invalida"
          },
          "codigo": {
            "type": "string",
            "example": "transporte"
          },
          "detalle": {
            "type": "string",
            "description": "Explicación del error de cliente (por ejemplo, el formato esperado del filtro)"
          }
        },
        "required": [
          "error"
        ]
      }
    }
  }
}
