{"openapi":"3.1.0","info":{"title":"Campodato API","version":"1.0.0","summary":"API pública del cuaderno de campo digital y agro-ERP Campodato.","description":"API REST pública de **Campodato** para integradores, gestorías y partners (canal B2B2C).\n\n## Autenticación\nTodas las rutas de datos requieren `Authorization: Bearer <JWT>`. El token incorpora la\norganización (`org`) y el rol del usuario; el servidor comprueba la pertenencia en cada petición.\n\n## Multi-tenant y RLS (fail-closed)\nCada petición opera dentro de UNA organización. El `organizationId` debe llegar en el cuerpo o\ncomo parámetro de consulta y DEBE coincidir con el `org` del token (en caso contrario, 403).\nLa base de datos aplica Row-Level Security forzado: un token solo ve filas de SU organización;\nsin contexto de tenant válido el resultado es 0 filas (fail-closed), nunca una fuga cross-tenant.\n\n## Webhooks\nLas suscripciones a eventos de dominio se entregan firmadas con **HMAC-SHA256** sobre el cuerpo\ncrudo de la petición (cabecera de firma); el secreto se entrega UNA sola vez al crear la\nsuscripción y no se vuelve a mostrar. Verifica la firma antes de procesar la entrega.\n\n## Idempotencia\nLas capturas (POST /datounico/hecho) y las Actions de escritura de la API v2 (POST\n/v2/public/ventas/clientes, /presupuestos, /facturas) son idempotentes por `clientOpId`: reintentar\nla misma operación devuelve el mismo resultado con `deduped: true`, sin reaplicar efectos.","contact":{"name":"Summum Marketing","url":"https://campodato.es","email":"soporte@campodato.es"},"license":{"name":"Propietaria · Summum Marketing","url":"https://campodato.es"}},"servers":[{"url":"https://api.campodato.es/api","description":"Producción"},{"url":"http://localhost:4000/api","description":"Desarrollo local"}],"tags":[{"name":"Dato único","description":"Captura de hechos de campo y su histórico append-only."},{"name":"Plazos","description":"Calendario de obligaciones legales (semáforos de cumplimiento)."},{"name":"Notificaciones","description":"Centro de avisos del tenant."},{"name":"Webhooks","description":"Suscripciones a eventos de dominio (entregas firmadas HMAC)."},{"name":"Caldo","description":"Calculadora de caldo y dosis: litros totales, número de cubas y cantidad exacta de cada producto por cuba y en total. Endpoint STATELESS de solo lectura (no escribe nada en el cuaderno ni valida compatibilidad de mezclas: eso es competencia del módulo fitosanitario)."},{"name":"Marketplace v2","description":"API pública v2: catálogo de conectores instalables, OAuth 2.1 (authorization-code + PKCE) para apps de terceros y webhooks ENTRANTES firmados HMAC. Amplía la superficie estable sin reescribir la v1."},{"name":"API v2 · datos","description":"API pública v2 de DATOS (solo lectura) bajo `/api/v2/public/*`. La consumen integradores con un TOKEN de API (`api_tokens`, esquema `bearerApiToken`) o un access token OAuth 2.1 del marketplace. El tenant lo fija SIEMPRE el token (nunca el request): cero fuga cross-tenant. Cada endpoint exige un scope `<modulo>:leer` (fail-closed) y está limitado por tasa (rate-limit). Recursos disponibles: cuaderno · plazos · parcelas · inventario · ventas · facturación · marketplace."}],"security":[{"bearerJwt":[]}],"components":{"securitySchemes":{"bearerJwt":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Token de acceso JWT emitido por `/auth/login`. Incorpora la organización y el rol; el servidor verifica firma, expiración, pertenencia (membership) y RBAC por módulo en cada petición."},"oauth2":{"type":"oauth2","description":"OAuth 2.1 authorization-code con PKCE (S256 obligatorio) para integraciones de terceros (Zapier/Make, ISVs). El titular de la organización consiente los scopes; el access token va hasheado en BD y caduca, y el refresh token ROTA en cada canje (su reutilización revoca la cadena). Los scopes son `<modulo>:<accion>` (p. ej. `cuaderno:leer`).","flows":{"authorizationCode":{"authorizationUrl":"/api/v2/oauth/autorizar","tokenUrl":"/api/v2/oauth/token","refreshUrl":"/api/v2/oauth/token","scopes":{"cuaderno:leer":"Leer el cuaderno de campo","cuaderno:escribir":"Anotar en el cuaderno de campo","parcelas:leer":"Leer recintos SIGPAC de la explotación","ventas:leer":"Leer facturas de venta y documentos del ciclo comercial","inventario:leer":"Leer movimientos de stock del almacén","facturacion:leer":"Leer registros Verifactu sellados (huella + estado AEAT)","tesoreria:escribir":"Volcar movimientos a tesorería (conectores bancarios)"}}}},"bearerApiToken":{"type":"http","scheme":"bearer","description":"Token de API de máquina (`cdp_<...>`) emitido por `POST /webhooks/tokens`. Acotado a scopes `<modulo>:<accion>` (fail-closed). La API v2 de datos (`/api/v2/public/*`) lo acepta como Bearer; resuelve la organización y los scopes por hash y opera SOLO dentro de esa organización."},"hmacWebhook":{"type":"apiKey","in":"header","name":"X-Campodato-Signature","description":"Esquema informativo (no es para autenticar peticiones a esta API, sino para describir cómo se firman las ENTREGAS salientes de webhook): cada entrega lleva una firma HMAC-SHA256 del cuerpo crudo con el secreto de la suscripción. Verifícala antes de procesar."}},"parameters":{"OrgIdQuery":{"name":"organizationId","in":"query","required":true,"description":"UUID de la organización (tenant). Debe coincidir con el `org` del token JWT.","schema":{"type":"string","format":"uuid"}},"TenantHeader":{"name":"x-org-id","in":"header","required":false,"description":"Cabecera de tenant opcional usada por el SDK web (espejo de `organizationId`).","schema":{"type":"string","format":"uuid"}},"HoldingQuery":{"name":"holding","in":"query","required":false,"description":"UUID de la explotación (holding) dentro de la organización.","schema":{"type":"string","format":"uuid"}},"LimitQuery":{"name":"limit","in":"query","required":false,"description":"Tamaño de página (def. 20, máx. 100).","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},"OffsetQuery":{"name":"offset","in":"query","required":false,"description":"Desplazamiento del paginado (keyset por seq DESC en el servidor).","schema":{"type":"integer","minimum":0,"default":0}}},"responses":{"Unauthorized":{"description":"Falta o es inválido el token de acceso (sin sesión válida).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"El token no pertenece a la organización indicada o el rol no autoriza la acción (RLS fail-closed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","description":"Forma estándar de error de la API (envoltorio de NestJS).","properties":{"statusCode":{"type":"integer","example":403},"message":{"type":"string","example":"El usuario no pertenece a la organización indicada"},"error":{"type":"string","example":"Forbidden"}},"required":["statusCode","message"]},"HechoDeCampo":{"type":"object","description":"Un hecho de campo capturado: el ÚNICO dato que el agricultor introduce; el servidor hace el fan-out determinista (cuaderno/SIEX, gate fito, balance-N, stock, coste, traza) en una sola transacción.","properties":{"organizationId":{"type":"string","format":"uuid","description":"Tenant. Debe coincidir con el token."},"holdingId":{"type":"string","format":"uuid","description":"Explotación."},"recintoId":{"type":"string","format":"uuid","description":"Recinto SIGPAC (opcional)."},"tipoHecho":{"type":"string","enum":["tratamiento","fertilizacion","riego","cosecha","labor","siembra"]},"businessDate":{"type":"string","format":"date","description":"Fecha de negocio (YYYY-MM-DD)."},"businessDatetimeUtc":{"type":"string","format":"date-time"},"datos":{"type":"object","additionalProperties":true,"description":"Carga específica del tipo de hecho."},"clientOpId":{"type":"string","description":"Clave de idempotencia generada por el cliente."},"deviceId":{"type":"string","description":"Identificador del dispositivo emisor (trazabilidad outbox)."}},"required":["organizationId","holdingId","tipoHecho","businessDate","businessDatetimeUtc","datos","clientOpId","deviceId"]},"ResumenDatoUnico":{"type":"object","description":"Resumen estructurado de TODO lo derivado del hecho (para confirmar en 1 toque).","properties":{"deduped":{"type":"boolean","description":"true si el hecho ya se había procesado (idempotencia por clientOpId)."},"eventoSeq":{"type":"integer","description":"Secuencia del evento append-only generado."}},"additionalProperties":true},"EventoDatoUnico":{"type":"object","description":"Fila del histórico append-only del fan-out (auditoría del dato único).","properties":{"seq":{"type":"integer"},"tipoHecho":{"type":"string"},"businessDate":{"type":"string","format":"date"},"holdingId":{"type":"string","format":"uuid"}},"additionalProperties":true},"PaginaEventos":{"type":"object","description":"Página de eventos (paginado por keyset en seq DESC).","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/EventoDatoUnico"}},"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"}},"required":["items"]},"ObligacionCalendario":{"type":"object","description":"Una obligación legal calendarizada con su semáforo de cumplimiento.","properties":{"codigoObligacion":{"type":"string","example":"MODELO_130_TRIMESTRAL"},"periodo":{"type":"string","example":"2026-T1"},"fechaLimite":{"type":"string","format":"date"},"estado":{"type":"string","enum":["pendiente","proximo","vencido","cumplido"]},"fechaCumplido":{"type":"string","format":"date"},"fuenteLegal":{"type":"string","description":"Referencia normativa de la obligación."}},"required":["codigoObligacion","periodo","fechaLimite","estado"]},"Notificacion":{"type":"object","description":"Aviso del centro de notificaciones del tenant.","properties":{"id":{"type":"string","format":"uuid"},"tipoEvento":{"type":"string"},"severidad":{"type":"string","enum":["info","aviso","critico"]},"titulo":{"type":"string"},"cuerpo":{"type":"string"},"enlace":{"type":"string"},"leida":{"type":"boolean"},"creadaEn":{"type":"string","format":"date-time"}},"required":["id","tipoEvento","severidad","titulo"]},"PaginaNotificaciones":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Notificacion"}},"total":{"type":"integer"}},"required":["items"]},"ProductoCalc":{"type":"object","description":"Un producto a incorporar al caldo, con su dosis tal y como la expresa el agricultor.","properties":{"id":{"type":"string","description":"Referencia opcional al producto fito del cuaderno; si se omite se usa el nombre."},"nombre":{"type":"string","example":"Mancozeb 80 WP"},"dosis":{"type":"string","description":"Cadena decimal (p. ej. \"0.2\", \"1.5\").","example":"0.2"},"unidad":{"type":"string","enum":["porcentaje","l_hl","kg_hl","l_ha","kg_ha"]},"orden":{"type":"integer","description":"Orden de incorporación a la cuba (menor = antes). Opcional."}},"required":["nombre","dosis","unidad"]},"CalcularCaldo":{"type":"object","description":"Parámetros del tratamiento para el cálculo de caldo y dosis por cuba.","properties":{"organizationId":{"type":"string","format":"uuid","description":"Tenant. Debe coincidir con el token."},"superficieHa":{"type":"string","description":"Superficie a tratar en hectáreas (cadena decimal, >= 0).","example":"4"},"volumenCaldoLHa":{"type":"string","description":"Volumen de caldo por hectárea en litros/ha.","example":"300"},"capacidadCubaL":{"type":"string","description":"Capacidad útil del tanque/cuba en litros (> 0).","example":"600"},"productos":{"type":"array","items":{"$ref":"#/components/schemas/ProductoCalc"}}},"required":["organizationId","superficieHa","volumenCaldoLHa","capacidadCubaL","productos"]},"DosisProducto":{"type":"object","description":"Cantidad calculada de un producto, por cuba llena, última cuba y total.","properties":{"id":{"type":"string"},"nombre":{"type":"string"},"magnitud":{"type":"string","enum":["litros","kilogramos"]},"cantidadPorCuba":{"type":"string"},"cantidadUltimaCuba":{"type":"string"},"cantidadTotal":{"type":"string"},"ordenIncorporacion":{"type":"integer"}},"required":["id","nombre","magnitud","cantidadPorCuba","cantidadUltimaCuba","cantidadTotal","ordenIncorporacion"]},"ResultadoCaldo":{"type":"object","description":"Resultado determinista del cálculo: litros totales, número de cubas y dosificación por producto en orden de incorporación. NO es un registro legal: el cuaderno de tratamiento sigue siendo la fuente del dato aplicado.","properties":{"litrosCaldoTotal":{"type":"string","example":"1200.00"},"capacidadCubaL":{"type":"string"},"numeroCubas":{"type":"integer"},"cubasCompletas":{"type":"integer"},"litrosUltimaCuba":{"type":"string"},"ultimaCubaLlena":{"type":"boolean"},"productos":{"type":"array","items":{"$ref":"#/components/schemas/DosisProducto"}},"ordenMezcla":{"type":"array","items":{"type":"string"}},"aviso":{"type":"string","description":"Recuerda que es aritmética de dosificación, no validación de compatibilidad ni de eficacia."}},"required":["litrosCaldoTotal","capacidadCubaL","numeroCubas","cubasCompletas","litrosUltimaCuba","ultimaCubaLlena","productos","ordenMezcla","aviso"]},"AltaSuscripcionWebhook":{"type":"object","description":"Alta de una suscripción webhook. El secreto HMAC se devuelve una sola vez en la respuesta.","properties":{"organizationId":{"type":"string","format":"uuid"},"suscripcion":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"URL destino (HTTPS; se valida anti-SSRF)."},"eventos":{"type":"array","items":{"type":"string"},"description":"Eventos de dominio a los que suscribirse."},"activa":{"type":"boolean","default":true}},"required":["url","eventos"]}},"required":["organizationId","suscripcion"]},"SuscripcionCreada":{"type":"object","description":"Suscripción creada. `secretoHmac` se muestra SOLO aquí: guárdalo para verificar las firmas.","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri"},"eventos":{"type":"array","items":{"type":"string"}},"secretoHmac":{"type":"string","description":"Secreto HMAC-SHA256 (visible una única vez)."}},"required":["id","secretoHmac"]},"ConectorCatalogo":{"type":"object","description":"Ficha de un conector instalable del catálogo (estática, global).","properties":{"clave":{"type":"string","description":"Identificador estable (slug), p. ej. `psd2-banca`."},"nombre":{"type":"string"},"descripcion":{"type":"string"},"categoria":{"type":"string","enum":["banca","normativa","automatizacion","erp","logistica","otros"]},"editor":{"type":"string"},"estadoAdaptador":{"type":"string","enum":["disponible","beta","dormido"]},"tipoCredencial":{"type":"string","enum":["ninguna","api_key","oauth2","mtls","hmac"]},"scopesRequeridos":{"type":"array","items":{"type":"string"}},"recibeEntrantes":{"type":"boolean"}},"required":["clave","nombre","categoria","estadoAdaptador","scopesRequeridos","recibeEntrantes"]},"PeticionToken":{"type":"object","description":"Cuerpo del endpoint OAuth `/v2/oauth/token` (según el grant).","properties":{"grant_type":{"type":"string","enum":["authorization_code","refresh_token"]},"client_id":{"type":"string"},"client_secret":{"type":"string","description":"Solo clientes confidenciales."},"code":{"type":"string","description":"grant authorization_code: el código emitido."},"redirect_uri":{"type":"string","format":"uri"},"code_verifier":{"type":"string","description":"PKCE: verifier del code_challenge S256."},"refresh_token":{"type":"string","description":"grant refresh_token: el refresh a rotar."},"scope":{"type":"string","description":"Lista de scopes separada por espacios (opcional)."}},"required":["grant_type","client_id"]},"RespuestaToken":{"type":"object","description":"Respuesta de token OAuth 2.1. Los tokens en claro se devuelven UNA vez.","properties":{"accessToken":{"type":"string"},"tokenType":{"type":"string","enum":["Bearer"]},"expiresIn":{"type":"integer","description":"Segundos hasta la expiración del access token."},"refreshToken":{"type":"string","description":"Refresh rotatorio."},"scope":{"type":"string"}},"required":["accessToken","tokenType","expiresIn","refreshToken","scope"]},"IntrospeccionToken":{"type":"object","description":"Introspección del token actual (GET /v2/public/me): qué organización y scopes lleva.","properties":{"tipo":{"type":"string","enum":["api_token","oauth"]},"organizationId":{"type":"string","format":"uuid"},"scopes":{"type":"array","items":{"type":"string"}},"clientId":{"type":"string","description":"client_id de la app (solo tokens OAuth)."},"apiVersion":{"type":"string","example":"2.0.0"}},"required":["tipo","organizationId","scopes","apiVersion"]},"OperacionCuadernoV2":{"type":"object","description":"Operación del cuaderno de campo (proyección pública del histórico append-only).","properties":{"id":{"type":"string","format":"uuid"},"seq":{"type":"integer","description":"Secuencia append-only (cursor estable)."},"tipoHecho":{"type":"string"},"businessDate":{"type":"string","format":"date","nullable":true},"holdingId":{"type":"string","format":"uuid","nullable":true},"recintoId":{"type":"string","nullable":true},"registradoEn":{"type":"string","format":"date-time","nullable":true},"derivadas":{"type":"integer"},"avisos":{"type":"integer"},"bloqueadas":{"type":"integer"}},"required":["id","seq","tipoHecho","derivadas","avisos","bloqueadas"]},"ObligacionV2":{"type":"object","description":"Obligación legal del calendario de cumplimiento (proyección pública).","properties":{"id":{"type":"string","format":"uuid"},"codigoObligacion":{"type":"string","example":"MODELO_130_TRIMESTRAL"},"periodo":{"type":"string","nullable":true,"example":"2026-T1"},"fechaLimite":{"type":"string","format":"date"},"estado":{"type":"string","enum":["pendiente","proximo","vencido","cumplido"]},"fechaCumplido":{"type":"string","format":"date","nullable":true},"holdingId":{"type":"string","format":"uuid","nullable":true}},"required":["id","codigoObligacion","fechaLimite","estado"]},"PaginaOperacionesV2":{"type":"object","description":"Página de operaciones del cuaderno (paginado por seq; `nextOffset` es el cursor).","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/OperacionCuadernoV2"}},"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"},"nextOffset":{"type":"integer","nullable":true}},"required":["items","total","limit","offset"]},"PaginaObligacionesV2":{"type":"object","description":"Página de obligaciones del calendario (paginado por fecha límite).","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ObligacionV2"}},"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"},"nextOffset":{"type":"integer","nullable":true}},"required":["items","total","limit","offset"]},"RecintoV2":{"type":"object","description":"Un recinto SIGPAC de la explotación (proyección pública, solo lectura).","properties":{"id":{"type":"string","format":"uuid"},"holdingId":{"type":"string","format":"uuid"},"alias":{"type":"string","nullable":true,"description":"Nombre propio del agricultor (p. ej. «la viña de arriba»)."},"refSigpac":{"type":"string","nullable":true,"description":"Referencia SIGPAC canónica (PP:MMM:AA:ZZ:PPP:RRR)."},"superficieHa":{"type":"string","nullable":true,"description":"Superficie en hectáreas (texto decimal)."},"usoSigpac":{"type":"string","nullable":true},"provincia":{"type":"integer","nullable":true},"municipio":{"type":"integer","nullable":true},"poligono":{"type":"integer","nullable":true},"parcela":{"type":"integer","nullable":true},"recinto":{"type":"integer","nullable":true}},"required":["id","holdingId"]},"PaginaRecintosV2":{"type":"object","description":"Página de recintos SIGPAC (paginado por fecha de alta DESC).","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/RecintoV2"}},"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"},"nextOffset":{"type":"integer","nullable":true}},"required":["items","total","limit","offset"]},"MovimientoStockV2":{"type":"object","description":"Un movimiento de stock del almacén (append-only, proyección pública).","properties":{"id":{"type":"string","format":"uuid"},"seq":{"type":"integer","description":"Secuencia append-only (cursor estable)."},"almacenId":{"type":"string","format":"uuid"},"insumoId":{"type":"string","format":"uuid"},"tipo":{"type":"string","description":"Tipo de movimiento (entrada_compra, salida_aplicacion, ajuste_positivo…)."},"cantidad":{"type":"string","description":"Cantidad con signo según tipo (texto decimal)."},"businessDate":{"type":"string","format":"date"},"motivo":{"type":"string","nullable":true},"registradoEn":{"type":"string","format":"date-time"}},"required":["id","seq","almacenId","insumoId","tipo","cantidad","businessDate","registradoEn"]},"PaginaMovimientosStockV2":{"type":"object","description":"Página de movimientos de stock (paginado por seq DESC).","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/MovimientoStockV2"}},"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"},"nextOffset":{"type":"integer","nullable":true}},"required":["items","total","limit","offset"]},"FacturaVentaV2":{"type":"object","description":"Una factura de venta (proyección pública de `facturas_venta`).","properties":{"id":{"type":"string","format":"uuid"},"holdingId":{"type":"string","format":"uuid"},"serie":{"type":"string","example":"A"},"numero":{"type":"integer"},"fechaExpedicion":{"type":"string","format":"date"},"baseImponible":{"type":"string","description":"Base imponible (texto decimal)."},"cuotaIva":{"type":"string","description":"Cuota de IVA (texto decimal)."},"recargoEquivalencia":{"type":"string","description":"Recargo de equivalencia (texto decimal); \"0\" si el cliente no está en ese régimen."},"importeTotal":{"type":"string","description":"Importe total (texto decimal)."},"registradaEn":{"type":"string","format":"date-time"}},"required":["id","holdingId","serie","numero","fechaExpedicion","importeTotal","registradaEn"]},"PaginaFacturasVentaV2":{"type":"object","description":"Página de facturas de venta (paginado por fecha de expedición DESC).","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/FacturaVentaV2"}},"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"},"nextOffset":{"type":"integer","nullable":true}},"required":["items","total","limit","offset"]},"RegistroFacturacionV2":{"type":"object","description":"Un registro legal Verifactu sellado (append-only; la huella encadenada no se expone aquí).","properties":{"id":{"type":"string","format":"uuid"},"seq":{"type":"integer","description":"Secuencia de sellado (cursor estable)."},"holdingId":{"type":"string","format":"uuid"},"serie":{"type":"string","example":"A"},"numero":{"type":"integer"},"nifEmisor":{"type":"string","example":"12345678A"},"tipo":{"type":"string","enum":["alta","anulacion","subsanacion"]},"tipoFactura":{"type":"string","nullable":true,"enum":["F1","F2","F3","R1","R2","R3","R4","R5"]},"fechaExpedicion":{"type":"string","format":"date"},"importeTotal":{"type":"string","description":"Importe total (texto decimal)."},"estadoAeat":{"type":"string","nullable":true,"description":"Estado AEAT: Correcto | AceptadoConErrores | Incorrecto | null (sin remitir)."},"registradoEn":{"type":"string","format":"date-time"}},"required":["id","seq","holdingId","serie","numero","nifEmisor","tipo","fechaExpedicion","importeTotal","registradoEn"]},"PaginaRegistrosFacturacionV2":{"type":"object","description":"Página de registros Verifactu (paginado por seq DESC).","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/RegistroFacturacionV2"}},"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"},"nextOffset":{"type":"integer","nullable":true}},"required":["items","total","limit","offset"]},"WebhookPayloadBase":{"type":"object","description":"Envoltorio común de todos los payloads de webhook saliente.","properties":{"version":{"type":"integer","enum":[1],"description":"Versión del contrato del payload."},"evento":{"type":"string","description":"Nombre del evento de dominio."},"entregaId":{"type":"string","format":"uuid","description":"Identificador del grupo de entrega (idempotencia del consumidor)."},"organizationId":{"type":"string","format":"uuid"},"emitidoEn":{"type":"string","format":"date-time","description":"Momento de emisión (ISO UTC)."},"datos":{"type":"object","additionalProperties":true,"description":"Datos específicos del evento."}},"required":["version","evento","entregaId","organizationId","emitidoEn","datos"]}}},"paths":{"/datounico/hecho":{"post":{"tags":["Dato único"],"operationId":"registrarHechoDeCampo","summary":"Captura un hecho de campo (idempotente por clientOpId).","description":"Registra UN hecho y dispara el fan-out determinista en la misma transacción del tenant. Reintentar con el mismo `clientOpId` devuelve el mismo resultado con `deduped: true`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HechoDeCampo"}}}},"responses":{"201":{"description":"Hecho capturado; resumen del fan-out.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResumenDatoUnico"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/datounico/eventos":{"get":{"tags":["Dato único"],"operationId":"listarEventosDatoUnico","summary":"Histórico append-only del fan-out (paginado por keyset, seq DESC).","parameters":[{"$ref":"#/components/parameters/OrgIdQuery"},{"$ref":"#/components/parameters/HoldingQuery"},{"$ref":"#/components/parameters/LimitQuery"},{"$ref":"#/components/parameters/OffsetQuery"}],"responses":{"200":{"description":"Página de eventos del histórico.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginaEventos"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/plazos/calendario":{"get":{"tags":["Plazos"],"operationId":"calendarioPlazos","summary":"Calendario de obligaciones legales del holding (semáforos de cumplimiento).","parameters":[{"$ref":"#/components/parameters/OrgIdQuery"},{"name":"holding","in":"query","required":true,"description":"UUID de la explotación (holding).","schema":{"type":"string","format":"uuid"}},{"name":"desde","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"hasta","in":"query","required":false,"schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Obligaciones del calendario ordenadas por fecha límite.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObligacionCalendario"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/notificaciones":{"get":{"tags":["Notificaciones"],"operationId":"listarNotificaciones","summary":"Lista paginada de notificaciones del tenant.","parameters":[{"$ref":"#/components/parameters/OrgIdQuery"},{"name":"soloNoLeidas","in":"query","required":false,"schema":{"type":"boolean"},"description":"Si es true, solo devuelve las no leídas."},{"$ref":"#/components/parameters/LimitQuery"},{"$ref":"#/components/parameters/OffsetQuery"}],"responses":{"200":{"description":"Página de notificaciones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginaNotificaciones"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/webhooks/suscripciones":{"post":{"tags":["Webhooks"],"operationId":"crearSuscripcionWebhook","summary":"Crea una suscripción a eventos de dominio (entregas firmadas HMAC).","description":"Da de alta una suscripción. La respuesta incluye `secretoHmac` UNA sola vez: úsalo para verificar la firma HMAC-SHA256 de cada entrega.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AltaSuscripcionWebhook"}}}},"responses":{"201":{"description":"Suscripción creada (incluye el secreto HMAC, visible una única vez).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuscripcionCreada"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/caldo/calcular":{"post":{"tags":["Caldo"],"operationId":"calcularCaldo","summary":"Calcula el caldo y la dosificación por cuba de un tratamiento (endpoint stateless).","description":"Recibe la superficie a tratar, el volumen de caldo por hectárea, la capacidad de la cuba y la lista de productos con su dosis, y devuelve los litros totales, el número de cubas y la cantidad exacta de cada producto por cuba (llena y última parcial) y en total, en orden de incorporación. Es una ayuda de cálculo de SOLO LECTURA: no escribe nada en el cuaderno ni valida compatibilidad de mezclas, uso autorizado o plazo de seguridad (eso es competencia del módulo fitosanitario).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalcularCaldo"}}}},"responses":{"200":{"description":"Desglose del caldo y la dosificación por cuba.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultadoCaldo"}}}},"400":{"description":"Parámetros inválidos (superficie/volumen/capacidad no decimales, unidad desconocida, producto sin nombre…)."},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v2/marketplace/catalogo":{"get":{"tags":["Marketplace v2"],"operationId":"catalogoConectores","summary":"Catálogo de conectores instalables (estático, igual para todas las organizaciones).","description":"Lista las fichas de conectores oficiales (PSD2 banca, FEGA/SIEX, AEAT sandbox, Peppol, Zapier/Make). Cada ficha indica categoría, scopes requeridos, si recibe webhooks entrantes y el estado del adaptador (algunos publicados como `dormido` hasta su activación por fases).","security":[{"bearerJwt":[]}],"responses":{"200":{"description":"Catálogo de conectores.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ConectorCatalogo"}}}}}}}},"/v2/oauth/token":{"post":{"tags":["Marketplace v2"],"operationId":"oauthToken","summary":"Canjea un código de autorización (PKCE) o un refresh token por tokens de acceso.","description":"Endpoint de token OAuth 2.1. Con `grant_type=authorization_code` canjea el `code` emitido tras el consentimiento verificando PKCE (`code_verifier` contra el `code_challenge` S256); con `grant_type=refresh_token` rota el refresh (el viejo se invalida; su reutilización revoca la cadena). El access token va acotado a los scopes consentidos (fail-closed) y caduca.","security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/PeticionToken"}},"application/json":{"schema":{"$ref":"#/components/schemas/PeticionToken"}}}},"responses":{"200":{"description":"Tokens emitidos (forma OAuth 2.1).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RespuestaToken"}}}},"400":{"description":"Error de grant (invalid_grant / invalid_scope / invalid_request)."},"401":{"description":"Cliente inválido (invalid_client)."}}}},"/v2/webhooks/in/{instalacionId}":{"post":{"tags":["Marketplace v2"],"operationId":"webhookEntrante","summary":"Recibe un webhook ENTRANTE de un sistema externo (firmado HMAC, idempotente).","description":"Endpoint público que recibe eventos de un sistema externo (un banco PSD2, un punto de acceso Peppol…). Verifica la firma HMAC-SHA256 sobre el cuerpo crudo con el secreto de la instalación y deduplica por id de evento (idempotencia). Una firma inválida devuelve 401; un id repetido se confirma con 200 y estado `duplicada` sin reprocesar. El registro es APPEND-ONLY.","security":[{"hmacWebhook":[]}],"parameters":[{"name":"instalacionId","in":"path","required":true,"description":"UUID de la instalación del conector que recibe el webhook.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Recepción registrada (estado: aceptada | duplicada | rechazada)."},"401":{"description":"Firma HMAC inválida."}}}},"/v2/public/me":{"get":{"tags":["API v2 · datos"],"operationId":"introspeccionTokenV2","summary":"Introspección del token actual (organización + scopes).","description":"Devuelve la organización y los scopes del token presentado. Cualquier token válido puede leerse a sí mismo (no exige un scope de datos concreto).","security":[{"bearerApiToken":[]},{"oauth2":[]}],"responses":{"200":{"description":"Identidad del token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntrospeccionToken"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v2/public/cuaderno/operaciones":{"get":{"tags":["API v2 · datos"],"operationId":"listarOperacionesCuadernoV2","summary":"Histórico del cuaderno de campo (paginado, solo lectura).","description":"Operaciones del cuaderno (fan-out del dato único) de la organización DEL TOKEN, ordenadas por seq DESC, paginadas y filtrables. Requiere el scope `cuaderno:leer`.","security":[{"bearerApiToken":["cuaderno:leer"]},{"oauth2":["cuaderno:leer"]}],"parameters":[{"$ref":"#/components/parameters/HoldingQuery"},{"name":"tipoHecho","in":"query","required":false,"schema":{"type":"string"}},{"name":"desde","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"hasta","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"$ref":"#/components/parameters/LimitQuery"},{"$ref":"#/components/parameters/OffsetQuery"}],"responses":{"200":{"description":"Página de operaciones del cuaderno.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginaOperacionesV2"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"description":"Demasiadas peticiones (rate-limit superado)."}}}},"/v2/public/plazos/obligaciones":{"get":{"tags":["API v2 · datos"],"operationId":"listarObligacionesV2","summary":"Calendario de obligaciones legales (paginado, solo lectura).","description":"Obligaciones del calendario de cumplimiento de la organización DEL TOKEN, ordenadas por fecha límite, paginadas y filtrables por estado/fecha. Requiere el scope `plazos:leer`.","security":[{"bearerApiToken":["plazos:leer"]},{"oauth2":["plazos:leer"]}],"parameters":[{"$ref":"#/components/parameters/HoldingQuery"},{"name":"estado","in":"query","required":false,"schema":{"type":"string","enum":["pendiente","proximo","vencido","cumplido"]}},{"name":"desde","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"hasta","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"$ref":"#/components/parameters/LimitQuery"},{"$ref":"#/components/parameters/OffsetQuery"}],"responses":{"200":{"description":"Página de obligaciones del calendario.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginaObligacionesV2"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"description":"Demasiadas peticiones (rate-limit superado)."}}}},"/v2/public/marketplace/conectores":{"get":{"tags":["API v2 · datos"],"operationId":"descubrirConectoresV2","summary":"Catálogo de conectores del marketplace (descubrimiento de integraciones).","description":"Mismo catálogo estático que `/v2/marketplace/catalogo`, accesible con un token de API/ OAuth para que un integrador descubra las integraciones disponibles. Requiere el scope `marketplace:leer`.","security":[{"bearerApiToken":["marketplace:leer"]},{"oauth2":["marketplace:leer"]}],"responses":{"200":{"description":"Catálogo de conectores.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ConectorCatalogo"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v2/public/parcelas/recintos":{"get":{"tags":["API v2 · datos"],"operationId":"listarRecintosV2","summary":"Recintos SIGPAC de la explotación (paginados, solo lectura).","description":"Recintos SIGPAC de la organización DEL TOKEN, ordenados por fecha de alta DESC. Requiere el scope `parcelas:leer`.","security":[{"bearerApiToken":["parcelas:leer"]},{"oauth2":["parcelas:leer"]}],"parameters":[{"$ref":"#/components/parameters/HoldingQuery"},{"$ref":"#/components/parameters/LimitQuery"},{"$ref":"#/components/parameters/OffsetQuery"}],"responses":{"200":{"description":"Página de recintos SIGPAC.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginaRecintosV2"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"description":"Demasiadas peticiones (rate-limit superado)."}}}},"/v2/public/inventario/movimientos":{"get":{"tags":["API v2 · datos"],"operationId":"listarMovimientosStockV2","summary":"Movimientos de stock del almacén (paginados, solo lectura).","description":"Movimientos de stock de la organización DEL TOKEN, ordenados por seq DESC, filtrables por insumo, tipo de movimiento y rango de fecha. Requiere el scope `inventario:leer`.","security":[{"bearerApiToken":["inventario:leer"]},{"oauth2":["inventario:leer"]}],"parameters":[{"$ref":"#/components/parameters/HoldingQuery"},{"name":"insumo","in":"query","required":false,"description":"UUID del insumo para filtrar.","schema":{"type":"string","format":"uuid"}},{"name":"tipo","in":"query","required":false,"description":"Tipo de movimiento (entrada_compra, salida_aplicacion…).","schema":{"type":"string"}},{"name":"desde","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"hasta","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"$ref":"#/components/parameters/LimitQuery"},{"$ref":"#/components/parameters/OffsetQuery"}],"responses":{"200":{"description":"Página de movimientos de stock.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginaMovimientosStockV2"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"description":"Demasiadas peticiones (rate-limit superado)."}}}},"/v2/public/ventas/clientes":{"post":{"tags":["API v2 · escritura"],"operationId":"crearClienteV2","summary":"Crea un cliente en la organización del token (Action Zapier/Make).","description":"Crea un cliente en la organización DEL TOKEN. La org se toma SIEMPRE del token (el body NO lleva `organizationId`; un valor contradictorio → 403). Requiere el scope `ventas:escribir` (POST → escribir; sin ese scope → 403, fail-closed). Idempotente por `clientOpId` (obligatorio, UUID): reintentar el mismo Zap con el mismo id devuelve el cliente original (`deduped: true`) en vez de duplicarlo — necesario porque Zapier/Make entregan sus Actions AT-LEAST-ONCE (reintentos tras un timeout o un 5xx).","security":[{"bearerApiToken":["ventas:escribir"]},{"oauth2":["ventas:escribir"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["nombre","clientOpId"],"properties":{"nombre":{"type":"string"},"nif":{"type":"string"},"regimen":{"type":"string","enum":["general","recargo_equivalencia","intracomunitario","exportacion","particular"]},"email":{"type":"string"},"telefono":{"type":"string"},"direccion":{"type":"string"},"codigoPostal":{"type":"string"},"poblacion":{"type":"string"},"provincia":{"type":"string"},"pais":{"type":"string","default":"ES"},"retencionAgrariaPct":{"type":"string","description":"Decimal en texto, p. ej. «2.00»."},"tarifaId":{"type":"string","format":"uuid"},"clientOpId":{"type":"string","format":"uuid","description":"Idempotencia: mismo id → mismo cliente (no duplica). Genéralo una vez por alta."}}}}}},"responses":{"201":{"description":"Cliente creado (o recuperado si `clientOpId` ya se había procesado; ver `deduped`).","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"clientOpId ausente o no es un UUID válido."},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"description":"Demasiadas peticiones (rate-limit superado)."}}}},"/v2/public/ventas/presupuestos":{"post":{"tags":["API v2 · escritura"],"operationId":"crearPresupuestoV2","summary":"Crea un presupuesto de venta con sus líneas (Action Zapier/Make).","description":"Crea un presupuesto en la organización DEL TOKEN (no lleva `organizationId` en el body). Requiere el scope `ventas:escribir` (fail-closed → 403 sin él). Idempotente por `clientOpId` (obligatorio, UUID): reintentar el mismo Zap con el mismo id devuelve el presupuesto original (`deduped: true`) en vez de duplicarlo.","security":[{"bearerApiToken":["ventas:escribir"]},{"oauth2":["ventas:escribir"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["holdingId","clienteId","numero","fechaEmision","lineas","clientOpId"],"properties":{"holdingId":{"type":"string","format":"uuid","description":"UUID de la explotación."},"clienteId":{"type":"string","format":"uuid"},"numero":{"type":"string"},"fechaEmision":{"type":"string","format":"date"},"fechaValidez":{"type":"string","format":"date"},"notas":{"type":"string"},"lineas":{"type":"array","items":{"type":"object","required":["producto","cantidad","precio","ivaPct"],"properties":{"producto":{"type":"string"},"cantidad":{"type":"string"},"precio":{"type":"string"},"ivaPct":{"type":"number","description":"0, 4, 10 o 21."},"descuentoPct":{"type":"string"}}}},"clientOpId":{"type":"string","format":"uuid","description":"Idempotencia: mismo id → mismo presupuesto (no duplica). Genéralo una vez por alta."}}}}}},"responses":{"201":{"description":"Presupuesto creado (o recuperado si `clientOpId` ya se había procesado; ver `deduped`).","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"clientOpId ausente o no es un UUID válido."},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"description":"Demasiadas peticiones (rate-limit superado)."}}}},"/v2/public/ventas/facturas":{"get":{"tags":["API v2 · datos"],"operationId":"listarFacturasVentaV2","summary":"Facturas de venta del ciclo comercial (paginadas, solo lectura).","description":"Facturas de venta de la organización DEL TOKEN, ordenadas por fecha de expedición DESC. Requiere el scope `ventas:leer`.","security":[{"bearerApiToken":["ventas:leer"]},{"oauth2":["ventas:leer"]}],"parameters":[{"$ref":"#/components/parameters/HoldingQuery"},{"name":"desde","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"hasta","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"$ref":"#/components/parameters/LimitQuery"},{"$ref":"#/components/parameters/OffsetQuery"}],"responses":{"200":{"description":"Página de facturas de venta.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginaFacturasVentaV2"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"description":"Demasiadas peticiones (rate-limit superado)."}}},"post":{"tags":["API v2 · escritura"],"operationId":"facturarV2","summary":"Sella una factura Verifactu (Action Zapier/Make; mismo circuito que el panel).","description":"Sella una factura Verifactu en la organización DEL TOKEN. Reutiliza el MISMO camino de sellado del panel (cadena de huella/numeración SIF append-only, sin atajos). Idempotente por `clientOpId`: reintentar con el mismo id devuelve el mismo resultado sin duplicar. La org la fija el token (el body NO lleva `organizationId`). Requiere el scope `ventas:escribir` (fail-closed → 403).","security":[{"bearerApiToken":["ventas:escribir"]},{"oauth2":["ventas:escribir"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["holdingId","clienteId","serie","nifEmisor","fechaExpedicion","fechaHoraHusoGen","clientOpId"],"properties":{"holdingId":{"type":"string","format":"uuid"},"clienteId":{"type":"string","format":"uuid"},"albaranId":{"type":"string","format":"uuid","description":"Si se omite, factura por líneas (venta directa/TPV)."},"serie":{"type":"string"},"nifEmisor":{"type":"string"},"tipoFactura":{"type":"string","enum":["F1","F2","F3","R1","R2","R3","R4","R5"],"default":"F1"},"fechaExpedicion":{"type":"string","format":"date"},"fechaHoraHusoGen":{"type":"string","description":"ISO-8601 con huso."},"lineas":{"type":"array","description":"Solo si se factura sin albarán (por líneas).","items":{"type":"object"}},"retencionPct":{"type":"string"},"facturaRectificadaId":{"type":"string","format":"uuid","description":"Factura ORIGINAL que se rectifica; obligatorio junto con motivoRectificacion y tipoRectificativa cuando tipoFactura es R1-R5 (art. 80 LIVA)."},"motivoRectificacion":{"type":"string","description":"Motivo de la rectificación; obligatorio junto con facturaRectificadaId."},"tipoRectificativa":{"type":"string","enum":["S","I"],"description":"S = por sustitución (importe completo corregido). I = por diferencias (solo el delta; admite importes negativos en línea). Obligatorio junto con facturaRectificadaId."},"clientOpId":{"type":"string","description":"Idempotencia: mismo id → mismo resultado (no duplica la factura)."}}}}}},"responses":{"201":{"description":"Factura sellada (huella Verifactu encadenada).","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"description":"Demasiadas peticiones (rate-limit superado)."}}}},"/v2/public/facturacion/registros":{"get":{"tags":["API v2 · datos"],"operationId":"listarRegistrosFacturacionV2","summary":"Registros Verifactu sellados (paginados, solo lectura).","description":"Registros legales Verifactu (invoice_record, append-only) de la organización DEL TOKEN, ordenados por seq DESC, filtrables por serie y rango de fecha. La cadena de huella SHA-256 NO se expone en este endpoint (disponible en el panel). Requiere el scope `facturacion:leer`.","security":[{"bearerApiToken":["facturacion:leer"]},{"oauth2":["facturacion:leer"]}],"parameters":[{"$ref":"#/components/parameters/HoldingQuery"},{"name":"serie","in":"query","required":false,"description":"Serie de facturación para filtrar (p. ej. «A»).","schema":{"type":"string"}},{"name":"desde","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"hasta","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"$ref":"#/components/parameters/LimitQuery"},{"$ref":"#/components/parameters/OffsetQuery"}],"responses":{"200":{"description":"Página de registros Verifactu.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginaRegistrosFacturacionV2"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"description":"Demasiadas peticiones (rate-limit superado)."}}}}},"webhooks":{"operacion.creada":{"post":{"summary":"Operación de cuaderno creada (fan-out del dato único completado).","description":"Se emite cuando un hecho de campo se registra correctamente (POST /datounico/hecho) y el fan-out determinista completa sin error. NO se emite si el hecho era un duplicado (deduped: true). El campo `datos` incluye: tipoHecho, holdingId, businessDate, seq.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookPayloadBase"}}}},"responses":{"200":{"description":"El consumidor ha procesado el evento."}}}},"factura.sellada":{"post":{"summary":"Factura sellada Verifactu (huella encadenada generada).","description":"Se emite cuando se sella una factura Verifactu (POST /ventas/facturar o POST /facturacion/facturas). El campo `datos` incluye: holdingId, serie, tipoFactura. Para los detalles completos (importe, huella) usa GET /v2/public/facturacion/registros con scope facturacion:leer.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookPayloadBase"}}}},"responses":{"200":{"description":"El consumidor ha procesado el evento."}}}},"plazo.vencido":{"post":{"summary":"Plazo / obligación legal superado su fecha límite.","description":"Se emite cuando un plazo de cumplimiento pasa a estado `vencido`. Datos: codigoObligacion, periodo, holdingId.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookPayloadBase"}}}},"responses":{"200":{"description":"El consumidor ha procesado el evento."}}}},"certificacion.por_vencer":{"post":{"summary":"Certificación de calidad próxima a caducar.","description":"Se emite cuando un sello de calidad entra en la ventana de aviso de vencimiento.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookPayloadBase"}}}},"responses":{"200":{"description":"El consumidor ha procesado el evento."}}}},"inventario.bajo_minimo":{"post":{"summary":"Insumo del almacén por debajo del stock mínimo.","description":"Se emite cuando la cantidad disponible de un insumo cae por debajo del mínimo configurado.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookPayloadBase"}}}},"responses":{"200":{"description":"El consumidor ha procesado el evento."}}}}}}