{
  "openapi": "3.1.0",
  "info": {
    "title": "ContraTodo API",
    "version": "1.0.0",
    "description": "API B2B de ContraTodo: contratos con firma electrónica, verificación de identidad y documentos vinculados, y recibos firmados que pueden cancelar pagarés CT-P. Autenticación con `x-api-key`. Referencia en prosa: docs/REFERENCIA_API_B2B.md; webhooks: docs/WEBHOOKS_Y_SEGURIDAD_B2B.md. Recibos y pagarés: docs/API_RECIBOS.md."
  },
  "servers": [
    {
      "url": "https://contratodo.click"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "paths": {
    "/api/v1/contracts/signing-sessions": {
      "post": {
        "operationId": "createSigningSession",
        "summary": "Crear contrato y sesión de firma",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 160
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSigningSession"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Sesión creada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SigningSession"
                }
              }
            }
          },
          "200": {
            "description": "Misma sesión, por idempotencia",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SigningSession"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "description": "Faltan datos obligatorios o tienen formato inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "403": {
            "description": "Sin permiso para la operación (paquete) o plantilla propia con clave real todavía no habilitada"
          },
          "404": {
            "description": "templateId inexistente o de otra empresa"
          },
          "409": {
            "description": "La plantilla todavía no fue revisada en el Portal Empresa, o la referencia externa ya se usó con otros datos"
          }
        }
      }
    },
    "/api/v1/contracts/signing-sessions/{id}": {
      "get": {
        "operationId": "getSigningSession",
        "summary": "Estado de una sesión de firma",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Sesión",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SigningSession"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "session": {
                          "type": "object",
                          "deprecated": true
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        },
        "description": "Devuelve la misma forma que la creación (`sessionId`, `status`, `portalUrl`, `document`...). `session` queda por compatibilidad y se retirará."
      }
    },
    "/api/v1/contract-flows": {
      "get": {
        "operationId": "listContractFlows",
        "summary": "Tipos de contrato (flowKey)",
        "description": "Lista de flowKey que acepta POST /api/v1/contracts/signing-sessions con `answers`. Pública y sin API key.",
        "security": [],
        "responses": {
          "200": {
            "description": "Lista de flujos",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "integer",
                      "description": "Versión de los esquemas. Sube cuando un cambio rompe a un integrador."
                    },
                    "flows": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "flowKey": {
                            "type": "string",
                            "example": "alquiler_vivienda"
                          },
                          "title": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/contract-flows/{flowKey}": {
      "get": {
        "operationId": "getContractFlowSchema",
        "summary": "Esquema de answers de un flowKey",
        "description": "JSON Schema (draft 2020-12) de `answers`. Cada opción tiene un código estable (`enum`) y su texto (`x-labels`); la API acepta cualquiera de los dos. Las fechas van en ISO 8601. `required` son los obligatorios siempre; `x-required-when`, los que dependen de otra respuesta.",
        "security": [],
        "parameters": [
          {
            "name": "flowKey",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "alquiler_vivienda"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "JSON Schema",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "flowKey inexistente"
          }
        }
      }
    },
    "/api/v1/templates": {
      "get": {
        "operationId": "listTemplates",
        "summary": "Plantillas de la empresa",
        "description": "Las plantillas que la empresa subió en el Portal Empresa (Plantillas). Solo se pueden usar las que tienen `reviewed: true`.",
        "responses": {
          "200": {
            "description": "Lista",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "templates": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "templateId": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "title": {
                            "type": "string"
                          },
                          "fields": {
                            "type": "integer"
                          },
                          "reviewed": {
                            "type": "boolean"
                          },
                          "updatedAt": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "API key inválida"
          }
        }
      }
    },
    "/api/v1/templates/{id}": {
      "get": {
        "operationId": "getTemplateSchema",
        "summary": "Esquema de answers de una plantilla de la empresa",
        "description": "JSON Schema (draft 2020-12) de `answers` para crear una sesión con `templateId`. Todos los campos son obligatorios. `x-fieldType` dice el tipo de dato (documento, monto, fecha, email…). Las fechas pueden ir en ISO 8601; un monto numérico se escribe como pesos.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "JSON Schema",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "Plantilla inexistente o de otra empresa"
          },
          "401": {
            "description": "API key inválida"
          }
        }
      }
    },
    "/api/v1/contracts/{id}": {
      "get": {
        "operationId": "getContract",
        "summary": "Datos de un contrato (partes, inmueble, plazo)",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Contrato",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "contract": {
                      "$ref": "#/components/schemas/Contract"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/contracts/{id}/documents": {
      "get": {
        "operationId": "listContractDocuments",
        "summary": "Documentos vinculados a un contrato",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Documentos en orden de creación",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "contractId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "documents": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LinkedDocument"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "createContractDocument",
        "summary": "Crear un documento vinculado (acta de entrega o recibo de devolución) con su sesión de firma",
        "description": "K4. Partes, domicilios e inmueble salen del contrato padre; `data` trae solo el inventario (acta) o el estado al devolver (recibo). Si no se indican firmantes, se toman las partes del contrato padre. Avisa por webhook con el evento `document.created`. Detalle de `data` en docs/REFERENCIA_API_B2B.md.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 160
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "documentKind",
                  "externalReference",
                  "data"
                ],
                "properties": {
                  "documentKind": {
                    "type": "string",
                    "enum": [
                      "acta_entrega_llaves",
                      "recibo_devolucion_llaves",
                      "carta_documento"
                    ]
                  },
                  "motive": {
                    "type": "string",
                    "enum": [
                      "intimacion_pago",
                      "pedido_reparacion",
                      "rescision_anticipada",
                      "restitucion_inmueble",
                      "reclamo_deposito"
                    ],
                    "description": "Solo carta_documento (K5). Apagada (422 carta_documento_pending_review) hasta que el abogado apruebe los textos."
                  },
                  "sender": {
                    "type": "string",
                    "enum": [
                      "locador",
                      "locatario"
                    ],
                    "description": "Solo carta_documento: opcional, tiene que coincidir con quien envía el motivo."
                  },
                  "externalReference": {
                    "type": "string",
                    "maxLength": 160
                  },
                  "data": {
                    "type": "object",
                    "description": "Acta: deliveryDate (YYYY-MM-DD), inventory (1 a 300 ítems; cada uno con photos opcional: hasta 3 URLs firmadas https del almacenamiento acordado, que ContraTodo copia y cita por su huella SHA-256), defaultMaintenance. Recibo: returnDate (YYYY-MM-DD), itemsReturned, deliveryDocumentId opcional (si falta, se usa la última acta del contrato). Carta documento: los datos del motivo (ver docs/REFERENCIA_API_B2B.md, K5)."
                  },
                  "signers": {
                    "type": "array",
                    "maxItems": 10,
                    "items": {
                      "$ref": "#/components/schemas/Signer"
                    }
                  },
                  "returnUrl": {
                    "type": "string",
                    "format": "uri"
                  },
                  "metadata": {
                    "type": "object"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Documento y sesión creados",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SigningSession"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "documentId": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "publicCode": {
                          "type": "string"
                        },
                        "parentContractId": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "parentPublicCode": {
                          "type": "string"
                        },
                        "documentKind": {
                          "type": "string"
                        },
                        "price": {
                          "type": "object",
                          "properties": {
                            "amount": {
                              "type": "number"
                            },
                            "currency": {
                              "type": "string"
                            }
                          }
                        },
                        "idempotentReplay": {
                          "type": "boolean"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "200": {
            "description": "Mismo documento, por idempotencia (idempotentReplay: true)"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "description": "Datos inválidos, contrato padre sin partes o sin acta con la que comparar",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/contracts/{id}/pdf": {
      "get": {
        "operationId": "downloadContractPdf",
        "summary": "PDF firmado",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "PDF firmado",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/identity/verify": {
      "post": {
        "operationId": "verifyIdentity",
        "summary": "Verificación de identidad (lectura de DNI y cotejo facial; sin prueba de vida)",
        "parameters": [
          {
            "name": "x-request-id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 160
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "dniFrontBase64",
                  "dniBackBase64",
                  "selfieBase64"
                ],
                "properties": {
                  "assuranceLevel": {
                    "type": "string",
                    "example": "contratodo_standard"
                  },
                  "dniFrontBase64": {
                    "type": "string"
                  },
                  "dniBackBase64": {
                    "type": "string"
                  },
                  "selfieBase64": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resultado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "requestId": {
                      "type": "string"
                    },
                    "decision": {
                      "type": "string"
                    },
                    "identity": {
                      "type": "object"
                    },
                    "scores": {
                      "type": "object"
                    },
                    "liveness": {
                      "type": "object"
                    },
                    "reasons": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "billing": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/billing/rates": {
      "get": {
        "operationId": "getRates",
        "summary": "Cotización de referencia",
        "security": [],
        "parameters": [
          {
            "name": "currency",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "ARS"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cotización",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "currency": {
                      "type": "string"
                    },
                    "rate": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/receipts": {
      "post": {
        "operationId": "issueReceipt",
        "summary": "Emitir un recibo firmado",
        "description": "Con promissoryNoteId, el recibo cancela ese pagaré CT-P y lo dice en su texto firmado.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 160
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateReceipt"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Recibo emitido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReceiptIssued"
                }
              }
            }
          },
          "200": {
            "description": "Mismo recibo, por idempotencia",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReceiptIssued"
                }
              }
            }
          },
          "400": {
            "description": "Cuerpo inválido; cada problema va en details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "details": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "402": {
            "description": "Saldo insuficiente (code: insufficient_balance)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "const": "insufficient_balance"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "get": {
        "operationId": "listReceipts",
        "summary": "Listar recibos emitidos por este cliente",
        "parameters": [
          {
            "name": "externalReference",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 160
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Recibos, del más nuevo al más viejo",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Receipt"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/receipts/{id}": {
      "get": {
        "operationId": "getReceipt",
        "summary": "Un recibo",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Recibo",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "receipt": {
                      "$ref": "#/components/schemas/Receipt"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/receipts/{id}/pdf": {
      "get": {
        "operationId": "downloadReceiptPdf",
        "summary": "PDF del recibo",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "PDF",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/receipts/{id}/send": {
      "post": {
        "operationId": "sendReceipt",
        "summary": "Enviar o reenviar el recibo por email",
        "description": "Cada envío genera un enlace nuevo y anula el anterior.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resultado del envío",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "delivery": {
                      "type": "object",
                      "properties": {
                        "sent": {
                          "type": "boolean"
                        },
                        "email": {
                          "type": "string"
                        },
                        "publicUrl": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/receipts/{id}/void": {
      "post": {
        "operationId": "voidReceipt",
        "summary": "Anular un recibo",
        "description": "El recibo no se borra: queda voided, con motivo y fecha. Si había dejado pagada una cuota, la cuota se reabre. Un recibo que canceló un pagaré no se anula por API (409): solo por un acuerdo firmado por acreedor y deudor, que se pide desde el panel (ver voidAgreementId).",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string",
                    "minLength": 5,
                    "maxLength": 500
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recibo anulado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "receipt": {
                      "$ref": "#/components/schemas/Receipt"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/webhooks/deliveries": {
      "get": {
        "operationId": "listWebhookDeliveries",
        "summary": "Entregas de webhooks",
        "description": "Las 50 entregas más recientes de tus webhooks. Si tu servidor no responde 2xx, se reintenta 3 veces al instante y después a los 1, 5, 15, 60, 180, 360 y 720 minutos.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "delivered",
                "failed"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Entregas",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "deliveries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDelivery"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/webhooks/deliveries/{id}/resend": {
      "post": {
        "operationId": "resendWebhookDelivery",
        "summary": "Reenviar una entrega de webhook",
        "description": "Envía de nuevo el mismo evento (mismo `id`), firmado con la hora actual.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Id"
          }
        ],
        "responses": {
          "200": {
            "description": "Resultado del reenvío",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "delivered": {
                      "type": "boolean"
                    },
                    "responseStatus": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/pricing": {
      "get": {
        "operationId": "getPricing",
        "summary": "Precios de firma vigentes",
        "description": "Precio de lista y precio de hoy de la firma básica y de la firma con identidad verificada, la promoción que rige y las firmas bonificadas. Pública y sin API key. Cuando cambian se avisa con el evento `pricing.updated`.",
        "security": [],
        "responses": {
          "200": {
            "description": "Precios",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignaturePricing"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/billing/statement": {
      "get": {
        "operationId": "getBillingStatement",
        "summary": "Estado de cuenta del mes",
        "description": "Las sesiones de firma creadas por el cliente en el mes (calendario de Argentina), con su estado y cantidad de firmantes, para conciliar.",
        "parameters": [
          {
            "name": "month",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
              "example": "2026-10"
            },
            "description": "AAAA-MM. Por defecto, el mes en curso."
          }
        ],
        "responses": {
          "200": {
            "description": "Estado de cuenta",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingStatement"
                }
              }
            }
          },
          "400": {
            "description": "Mes inválido"
          },
          "401": {
            "description": "Falta la API key o es inválida"
          }
        }
      }
    },
    "/api/v1/contracts/signing-sessions/{id}/cancel": {
      "post": {
        "operationId": "cancelSigningSession",
        "summary": "Anular una sesión de firma",
        "description": "Anula una sesión que todavía no se firmó del todo: el contrato pasa a cancelado, las invitaciones pendientes se revocan y se avisa contract.cancelled. Idempotente.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "maxLength": 500
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Anulada (o ya estaba anulada: alreadyCancelled)"
          },
          "404": {
            "description": "Sesión de otro cliente o inexistente"
          },
          "409": {
            "description": "El contrato ya está firmado"
          }
        }
      }
    },
    "/api/v1/seals": {
      "post": {
        "operationId": "createSeal",
        "summary": "Sellar la huella de un documento (C8)",
        "description": "Sello de tiempo de una autoridad externa (RFC 3161) sobre la huella SHA-256 de un recibo o libre deuda del integrador. ContraTodo recibe solo la huella, nunca el contenido. Requiere `Idempotency-Key`; la misma `externalReference` con la misma huella devuelve el sello existente y con otra huella responde 409. Si la autoridad no responde, 503 `seal_unavailable`: se reintenta más tarde.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 160
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "hash",
                  "externalReference",
                  "documentType"
                ],
                "properties": {
                  "hash": {
                    "type": "string",
                    "pattern": "^[0-9a-fA-F]{64}$"
                  },
                  "externalReference": {
                    "type": "string",
                    "maxLength": 160
                  },
                  "documentType": {
                    "type": "string",
                    "enum": [
                      "rent_receipt",
                      "rent_clearance"
                    ]
                  },
                  "parentContractId": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Sello creado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Seal"
                }
              }
            }
          },
          "200": {
            "description": "Reintento: el sello ya existía",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Seal"
                }
              }
            }
          },
          "404": {
            "description": "parentContractId de otro cliente o inexistente"
          },
          "409": {
            "description": "La externalReference ya tiene un sello con otra huella"
          },
          "422": {
            "description": "Datos inválidos (details)"
          },
          "503": {
            "description": "La autoridad de sellado no respondió"
          }
        }
      }
    },
    "/api/v1/seals/{id}": {
      "get": {
        "operationId": "getSeal",
        "summary": "Un sello del cliente (C8)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "El sello",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Seal"
                }
              }
            }
          },
          "404": {
            "description": "No existe o es de otro cliente"
          }
        }
      }
    },
    "/api/v1/contracts/{id}/installments": {
      "get": {
        "operationId": "listInstallments",
        "summary": "Cuotas de un contrato (K7.1)",
        "description": "Las cuotas del contrato firmado, del mismo motor que escribió el contrato, con sus ajustes y los pagos registrados. Los períodos futuros con índice todavía no tienen cuotas: aparecen al ajustarse (webhook `installments.created`).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cronograma",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstallmentSchedule"
                }
              }
            }
          },
          "404": {
            "description": "Contrato de otro cliente o inexistente"
          }
        }
      }
    },
    "/api/v1/contracts/{id}/installments/{installmentId}/payments": {
      "post": {
        "operationId": "createInstallmentPayment",
        "summary": "Registrar un pago de una cuota (K7.3)",
        "description": "El integrador emite su propio recibo y manda la huella. Si lo pagado cubre la cuota, queda pagada y se envía `installment.paid`; si no, queda el pago parcial. Requiere `Idempotency-Key`: repetirlo devuelve el mismo pago con `idempotentReplay: true`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "installmentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 160
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "date",
                  "amount"
                ],
                "properties": {
                  "date": {
                    "type": "string",
                    "format": "date"
                  },
                  "amount": {
                    "type": "number",
                    "exclusiveMinimum": 0
                  },
                  "externalReference": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 160
                  },
                  "receiptSha256": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "pattern": "^[0-9a-fA-F]{64}$"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Pago registrado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "idempotentReplay": {
                      "type": "boolean"
                    },
                    "payment": {
                      "$ref": "#/components/schemas/InstallmentPayment"
                    },
                    "installment": {
                      "$ref": "#/components/schemas/Installment"
                    }
                  }
                }
              }
            }
          },
          "200": {
            "description": "Reintento con la misma Idempotency-Key"
          },
          "400": {
            "description": "Falta Idempotency-Key"
          },
          "404": {
            "description": "Contrato o cuota inexistente"
          },
          "409": {
            "description": "Cuota pagada o anulada, o cambió a la vez (volver a intentar)"
          },
          "422": {
            "description": "Datos inválidos (details)"
          }
        }
      }
    },
    "/api/v1/contracts/{id}/installments/{installmentId}/payments/{paymentId}/void": {
      "post": {
        "operationId": "voidInstallmentPayment",
        "summary": "Anular un pago registrado por error (K7.3)",
        "description": "La cuota vuelve a pendiente o vencida si ya no está cubierta. Repetirlo no cambia nada.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "installmentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "paymentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Pago anulado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "installment": {
                      "$ref": "#/components/schemas/Installment"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Contrato, cuota o pago inexistente"
          },
          "409": {
            "description": "La cuota cambió a la vez (volver a intentar)"
          }
        }
      }
    },
    "/api/v1/contracts/{id}/adjustments": {
      "post": {
        "operationId": "createManualAdjustment",
        "summary": "Cargar a mano el índice de un ajuste (K7.4)",
        "description": "Para el ajuste que espera (`nextAdjustment.waitingIndex`): `startValue` y `adjustmentValue` del índice, o `coefficient` para Casa Propia. El servidor recalcula con la fórmula de la cláusula, crea las cuotas del período, anota que el valor se cargó a mano y envía `installments.created`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "startValue": {
                    "type": "number"
                  },
                  "adjustmentValue": {
                    "type": "number"
                  },
                  "coefficient": {
                    "type": "number"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Ajuste aplicado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "amount": {
                      "type": "number"
                    },
                    "formula": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Contrato de otro cliente o inexistente"
          },
          "409": {
            "description": "Contrato sin firmar o sin un ajuste esperando valores"
          },
          "422": {
            "description": "Valores inválidos"
          }
        }
      }
    }
  },
  "webhooks": {
    "contract.partially_signed": {
      "post": {
        "summary": "Firmó una parte; faltan otras",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recibido"
          }
        }
      }
    },
    "contract.signed": {
      "post": {
        "summary": "Firmaron todas las partes",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recibido"
          }
        }
      }
    },
    "contract.cancelled": {
      "post": {
        "summary": "El contrato se canceló antes de firmarse",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recibido"
          }
        }
      }
    },
    "pricing.updated": {
      "post": {
        "summary": "Cambiaron los precios de firma",
        "description": "Se envía a cada cliente activo con webhook. `data` tiene el mismo formato que GET /api/v1/pricing.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string",
                    "const": "pricing.updated"
                  },
                  "occurredAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "$ref": "#/components/schemas/SignaturePricing"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recibido"
          }
        }
      }
    },
    "installments.created": {
      "post": {
        "summary": "Cuotas nuevas (K7.2)",
        "description": "Al firmarse el contrato (`reason: signed`) y en cada ajuste (`reason: adjusted`, con `period` y `adjustment`). ContraTodo no le escribe al deudor de estos contratos: lo hace el integrador.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string",
                    "const": "installments.created"
                  },
                  "occurredAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "contractId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "reason": {
                        "type": "string",
                        "enum": [
                          "signed",
                          "adjusted"
                        ]
                      },
                      "period": {
                        "type": "integer"
                      },
                      "adjustment": {
                        "type": "object"
                      },
                      "installments": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "number": {
                              "type": "integer"
                            },
                            "total": {
                              "type": "integer"
                            },
                            "dueDate": {
                              "type": "string",
                              "format": "date"
                            },
                            "amount": {
                              "type": "number"
                            },
                            "currency": {
                              "type": "string"
                            },
                            "period": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recibido"
          }
        }
      }
    },
    "installment.overdue": {
      "post": {
        "summary": "Cuota vencida (K7.2)",
        "description": "El día siguiente al vencimiento, sin pago registrado. Una sola vez por cuota.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string",
                    "const": "installment.overdue"
                  },
                  "occurredAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "contractId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "installment": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "number": {
                            "type": "integer"
                          },
                          "total": {
                            "type": "integer"
                          },
                          "dueDate": {
                            "type": "string",
                            "format": "date"
                          },
                          "amount": {
                            "type": "number"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "period": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recibido"
          }
        }
      }
    },
    "installment.paid": {
      "post": {
        "summary": "Cuota pagada (K7.2)",
        "description": "Cuando los pagos registrados cubren la cuota.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string",
                    "const": "installment.paid"
                  },
                  "occurredAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "contractId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "installment": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "number": {
                            "type": "integer"
                          },
                          "total": {
                            "type": "integer"
                          },
                          "dueDate": {
                            "type": "string",
                            "format": "date"
                          },
                          "amount": {
                            "type": "number"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "period": {
                            "type": "integer"
                          }
                        }
                      },
                      "paymentId": {
                        "type": "string",
                        "format": "uuid"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recibido"
          }
        }
      }
    },
    "adjustment.waiting_index": {
      "post": {
        "summary": "Un ajuste espera el índice a mano (K7.2)",
        "description": "Casa Propia (sin fuente automática) o un índice que el BCRA o el INDEC no publicaron en 3 días. Una sola vez por período; se carga con POST /api/v1/contracts/{id}/adjustments.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string",
                    "const": "adjustment.waiting_index"
                  },
                  "occurredAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "contractId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "period": {
                        "type": "integer"
                      },
                      "index": {
                        "type": "string"
                      },
                      "adjustmentDate": {
                        "type": "string",
                        "format": "date"
                      },
                      "reason": {
                        "type": "string",
                        "enum": [
                          "no_automatic_source",
                          "index_not_published"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recibido"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key"
      }
    },
    "parameters": {
      "Id": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Error",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "error"
              ],
              "properties": {
                "error": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "Signer": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "phone": {
            "type": "string"
          },
          "role": {
            "type": "string"
          },
          "requireAccessCode": {
            "type": "boolean"
          },
          "signatureLevel": {
            "type": "string",
            "enum": [
              "basic",
              "plus"
            ],
            "default": "basic",
            "description": "`plus`: la identidad del firmante queda cotejada con el registro oficial antes de firmar. Si hoy no se puede (ver `firmaPlusDisponible` en GET /api/v1/pricing), la sesión responde 422 con `code: signature_plus_unavailable`."
          }
        }
      },
      "CreateSigningSession": {
        "type": "object",
        "required": [
          "externalReference",
          "title",
          "signers"
        ],
        "properties": {
          "externalReference": {
            "type": "string",
            "maxLength": 160
          },
          "title": {
            "type": "string",
            "maxLength": 200
          },
          "flowKey": {
            "type": "string",
            "description": "Flujo de contrato, p. ej. alquiler_vivienda. Con answers."
          },
          "templateId": {
            "type": "string",
            "format": "uuid",
            "description": "Plantilla de la empresa (Portal Empresa → Plantillas), en lugar de `flowKey` o `contractMarkdown`. `answers` trae todos sus campos (esquema en `GET /api/v1/templates/{id}`). La plantilla tiene que estar revisada (409 si no). Con clave real responde 403 hasta que se habiliten las plantillas propias en producción; con clave de prueba funciona."
          },
          "answers": {
            "type": "object",
            "additionalProperties": true
          },
          "completion": {
            "type": "string",
            "enum": [
              "api",
              "portal"
            ],
            "default": "api",
            "description": "`api`: `answers` tiene que llegar completo o responde 422. `portal`: se aceptan `answers` incompletos; la sesión queda en `collecting_data` y en `portalUrl` se completan los datos que faltan antes de redactar el contrato. Un dato con formato inválido se rechaza igual (422)."
          },
          "contractMarkdown": {
            "type": "string",
            "minLength": 200
          },
          "signers": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/Signer"
            }
          },
          "returnUrl": {
            "type": "string",
            "format": "uri"
          },
          "certification": {
            "type": "object",
            "properties": {
              "free": {
                "type": "boolean"
              },
              "amount": {
                "type": "number"
              },
              "currency": {
                "type": "string"
              }
            }
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "SigningSession": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "sessionId": {
            "type": "string",
            "format": "uuid"
          },
          "externalReference": {
            "type": "string"
          },
          "contractId": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "collecting_data",
              "pending_payment",
              "pending_signatures",
              "partially_signed",
              "signed",
              "cancelled",
              "failed"
            ]
          },
          "portalUrl": {
            "type": "string",
            "format": "uri"
          },
          "returnUrl": {
            "type": "string"
          },
          "signerInvitations": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "missing": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Solo con `completion: portal`: campos que faltan y se completan en el portal."
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "certification": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "number"
              },
              "currency": {
                "type": "string"
              },
              "free": {
                "type": "boolean"
              }
            }
          },
          "document": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "pdfUrl": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "pdfHash": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "livemode": {
            "type": "boolean",
            "description": "false para lo creado con una clave de prueba (ct_test_): sin cobro y con el PDF marcado PRUEBA — SIN VALIDEZ."
          }
        }
      },
      "ValidationError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "missing": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "formatIssues": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "campo": {
                  "type": "string"
                },
                "mensaje": {
                  "type": "string"
                }
              }
            }
          },
          "unknownFields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Solo con `templateId`: claves de `answers` que no existen en la plantilla."
          }
        }
      },
      "Party": {
        "type": "object",
        "properties": {
          "rol": {
            "type": [
              "string",
              "null"
            ]
          },
          "nombre": {
            "type": "string"
          },
          "documento": {
            "type": [
              "string",
              "null"
            ]
          },
          "domicilio": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Contract": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "publicCode": {
            "type": "string"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "flowKey": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "parties": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Party"
            }
          },
          "property": {
            "type": [
              "object",
              "null"
            ]
          },
          "startDate": {
            "type": [
              "string",
              "null"
            ]
          },
          "durationMonths": {
            "type": [
              "number",
              "null"
            ]
          },
          "depositAmount": {
            "type": [
              "number",
              "null"
            ]
          },
          "parentContractId": {
            "type": [
              "string",
              "null"
            ]
          },
          "pdfHash": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "LinkedDocument": {
        "type": "object",
        "properties": {
          "documentId": {
            "type": "string",
            "format": "uuid"
          },
          "publicCode": {
            "type": "string"
          },
          "documentKind": {
            "type": [
              "string",
              "null"
            ]
          },
          "motive": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "signedAt": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "description": "Firmado con x-contratodo-signature: sha256=HMAC(secret, timestamp + '.' + body).",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time"
          },
          "sessionId": {
            "type": "string"
          },
          "externalReference": {
            "type": "string"
          },
          "contractId": {
            "type": "string"
          },
          "data": {
            "type": "object"
          },
          "livemode": {
            "type": "boolean",
            "description": "false si el evento es de un contrato de prueba."
          }
        }
      },
      "ReceiptParty": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "document": {
            "type": [
              "string",
              "null"
            ],
            "description": "DNI o CUIT"
          }
        }
      },
      "CreateReceipt": {
        "type": "object",
        "required": [
          "date",
          "creditor",
          "debtor",
          "concept",
          "amount",
          "currency",
          "paymentMethod"
        ],
        "properties": {
          "externalReference": {
            "type": "string",
            "maxLength": 160,
            "description": "Referencia propia de la integración; se puede filtrar por ella."
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "Fecha del pago."
          },
          "place": {
            "type": "string"
          },
          "creditor": {
            "$ref": "#/components/schemas/ReceiptParty"
          },
          "debtor": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ReceiptParty"
              },
              {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              }
            ]
          },
          "concept": {
            "type": "string",
            "enum": [
              "rent",
              "expenses",
              "installment",
              "down_payment",
              "security_deposit",
              "loan_repayment",
              "other"
            ]
          },
          "detail": {
            "type": "string",
            "description": "Obligatorio con concept = other."
          },
          "period": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}$"
          },
          "amount": {
            "type": "number",
            "exclusiveMinimum": 0
          },
          "currency": {
            "type": "string",
            "enum": [
              "ARS",
              "USD"
            ]
          },
          "paymentMethod": {
            "type": "string",
            "enum": [
              "transfer",
              "cash",
              "check",
              "other"
            ]
          },
          "paymentReference": {
            "type": "string"
          },
          "fullSettlement": {
            "type": "boolean",
            "description": "Recibo por saldo (art. 899 a CCyC)."
          },
          "includesAccessories": {
            "type": "boolean",
            "description": "false sin reserva extingue intereses y accesorios (art. 899 c CCyC)."
          },
          "creditorReservations": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "string",
              "maxLength": 500
            }
          },
          "debtorReservations": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "string",
              "maxLength": 500
            }
          },
          "unpaidPeriods": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}$"
            }
          },
          "contractCode": {
            "type": "string"
          },
          "obligationId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Cuota de Finanzas que se paga."
          },
          "promissoryNoteId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Pagaré CT-P que este pago cancela. Tiene que ser de la cuenta y estar vigente; el importe tiene que cubrirlo en la misma moneda."
          },
          "contractId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "signature": {
            "type": "object",
            "properties": {
              "png": {
                "type": "string",
                "description": "data:image/png;base64,…"
              }
            }
          },
          "send": {
            "type": "boolean",
            "description": "Con debtor.email, manda el recibo por email a quien pagó."
          },
          "branding": {
            "type": "object",
            "description": "Marca compartida (planes premium).",
            "properties": {
              "name": {
                "type": "string"
              },
              "logoPng": {
                "type": "string"
              },
              "headerColor": {
                "type": "string"
              },
              "accentColor": {
                "type": "string"
              }
            }
          }
        }
      },
      "Receipt": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "receipt"
          },
          "number": {
            "type": "string",
            "description": "Serie de la cuenta, por ejemplo 0007."
          },
          "contratodoNumber": {
            "type": [
              "string",
              "null"
            ],
            "description": "Número global, por ejemplo CT-R-26-000123."
          },
          "verificationCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "Por ejemplo 7KQ4-M2XD."
          },
          "status": {
            "type": "string",
            "enum": [
              "issued",
              "voided"
            ]
          },
          "voidedAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "voidReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "previousReceiptSha256": {
            "type": [
              "string",
              "null"
            ]
          },
          "externalReference": {
            "type": [
              "string",
              "null"
            ]
          },
          "issuedAt": {
            "type": "string",
            "format": "date-time"
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "creditor": {
            "$ref": "#/components/schemas/ReceiptParty"
          },
          "debtor": {
            "$ref": "#/components/schemas/ReceiptParty"
          },
          "concept": {
            "type": "string"
          },
          "period": {
            "type": [
              "string",
              "null"
            ]
          },
          "amount": {
            "type": "number"
          },
          "currency": {
            "type": "string",
            "enum": [
              "ARS",
              "USD"
            ]
          },
          "paymentMethod": {
            "type": "string"
          },
          "fullSettlement": {
            "type": "boolean"
          },
          "text": {
            "type": "string",
            "description": "Texto firmado."
          },
          "sha256": {
            "type": "string"
          },
          "signature": {
            "type": "object",
            "properties": {
              "method": {
                "type": "string",
                "enum": [
                  "api_account",
                  "passkey",
                  "pin",
                  "drawn"
                ]
              },
              "hasImage": {
                "type": "boolean"
              },
              "identityVerified": {
                "type": "boolean"
              }
            }
          },
          "contractId": {
            "type": [
              "string",
              "null"
            ]
          },
          "obligationId": {
            "type": [
              "string",
              "null"
            ]
          },
          "promissoryNoteId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pagaré CT-P que este recibo canceló."
          },
          "voidAgreementId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Acuerdo de anulación pedido desde el panel para un recibo que canceló un pagaré. Mientras las dos partes no lo firmen, el recibo sigue issued; con las dos firmas pasa a voided y el pagaré vuelve a vigente."
          },
          "delivery": {
            "type": "object",
            "properties": {
              "email": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "sentAt": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "viewedAt": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "debtorResponse": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "accepted",
                  "reservations"
                ]
              },
              "reservations": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "at": {
                "type": "string"
              },
              "sha256": {
                "type": "string"
              }
            }
          },
          "links": {
            "type": "object",
            "properties": {
              "self": {
                "type": "string"
              },
              "pdf": {
                "type": "string"
              },
              "send": {
                "type": "string"
              },
              "void": {
                "type": "string"
              },
              "verify": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        }
      },
      "ReceiptIssued": {
        "type": "object",
        "properties": {
          "receipt": {
            "$ref": "#/components/schemas/Receipt"
          },
          "idempotentReplay": {
            "type": "boolean"
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "article": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                },
                "suggestedReservation": {
                  "type": "string"
                }
              }
            }
          },
          "obligation": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "effect": {
                "type": "string",
                "enum": [
                  "paid",
                  "partial",
                  "currency_mismatch"
                ]
              }
            }
          },
          "promissoryNote": {
            "type": [
              "object",
              "null"
            ],
            "description": "Pagaré cancelado por este recibo.",
            "properties": {
              "id": {
                "type": "string"
              },
              "contratodoNumber": {
                "type": "string",
                "description": "Por ejemplo CT-P-26-000123."
              },
              "status": {
                "const": "cancelled"
              }
            }
          },
          "delivery": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "sent": {
                "type": "boolean"
              },
              "publicUrl": {
                "type": "string"
              }
            }
          },
          "charge": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "amountUsd": {
                "type": "number"
              },
              "currency": {
                "const": "USD"
              }
            }
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "eventId": {
            "type": [
              "string",
              "null"
            ],
            "description": "El `id` del evento enviado; sirve para deduplicar."
          },
          "type": {
            "type": "string",
            "example": "contract.signed"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "delivered",
              "failed"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "lastResponseStatus": {
            "type": [
              "integer",
              "null"
            ]
          },
          "lastAttemptAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "nextAttemptAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Próximo reintento automático, si queda alguno."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SignaturePricing": {
        "type": "object",
        "description": "Precios de firma que rigen hoy. `huella` cambia solo cuando cambia algún precio, la promoción o las firmas bonificadas.",
        "properties": {
          "version": {
            "type": "integer",
            "example": 1
          },
          "moneda": {
            "type": "string",
            "example": "ARS"
          },
          "firma": {
            "type": "object",
            "properties": {
              "basica": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "precioDeLista": {
                    "type": "number",
                    "description": "Precio de lista por firma, sin descuentos."
                  },
                  "precio": {
                    "type": "number",
                    "description": "Lo que se cobra hoy por firma."
                  },
                  "descuentoPorcentaje": {
                    "type": "number"
                  }
                }
              },
              "verificada": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "precioDeLista": {
                    "type": "number",
                    "description": "Precio de lista por firma, sin descuentos."
                  },
                  "precio": {
                    "type": "number",
                    "description": "Lo que se cobra hoy por firma."
                  },
                  "descuentoPorcentaje": {
                    "type": "number"
                  }
                },
                "description": "Firma con identidad verificada."
              }
            }
          },
          "promocion": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "porcentaje": {
                "type": "number",
                "example": 50
              },
              "hasta": {
                "type": "string",
                "example": "2027-02-01"
              }
            }
          },
          "firmasBonificadas": {
            "type": "integer",
            "description": "Firmas sin cargo por contrato que rigen hoy."
          },
          "huella": {
            "type": "string"
          },
          "firmaPlusDisponible": {
            "type": "boolean",
            "description": "Si hoy se puede pedir signatureLevel \"plus\"."
          }
        }
      },
      "BillingStatement": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "month": {
            "type": "string",
            "example": "2026-10"
          },
          "sessions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "sessionId": {
                  "type": "string"
                },
                "contractId": {
                  "type": "string"
                },
                "externalReference": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                },
                "signers": {
                  "type": "integer",
                  "description": "Cantidad de firmantes."
                },
                "plusSigners": {
                  "type": "integer",
                  "description": "Firmantes con firma Plus."
                },
                "test": {
                  "type": "boolean",
                  "description": "Creada con una clave de prueba (no se cobra)."
                },
                "createdAt": {
                  "type": "string",
                  "format": "date-time"
                },
                "completedAt": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                }
              }
            }
          },
          "totals": {
            "type": "object",
            "description": "Sin contar las sesiones de prueba.",
            "properties": {
              "sessions": {
                "type": "integer"
              },
              "signed": {
                "type": "integer"
              },
              "cancelled": {
                "type": "integer"
              },
              "open": {
                "type": "integer"
              },
              "signatures": {
                "type": "integer"
              },
              "signaturesNotCancelled": {
                "type": "integer"
              },
              "test": {
                "type": "integer"
              },
              "installmentContracts": {
                "type": "integer"
              },
              "installmentAmount": {
                "type": "number"
              }
            }
          },
          "installmentManagement": {
            "type": "object",
            "description": "K7.5: gestión de cuotas del mes, por contrato con alguna cuota que vence en el mes (sin los de prueba), al precio que rige ese mes.",
            "properties": {
              "contracts": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "uuid"
                }
              },
              "unitPrice": {
                "type": "number"
              },
              "amount": {
                "type": "number"
              }
            }
          }
        }
      },
      "Seal": {
        "type": "object",
        "properties": {
          "sealId": {
            "type": "string",
            "format": "uuid"
          },
          "hash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "externalReference": {
            "type": "string"
          },
          "documentType": {
            "type": "string",
            "enum": [
              "rent_receipt",
              "rent_clearance"
            ]
          },
          "parentContractId": {
            "type": [
              "string",
              "null"
            ]
          },
          "sealedAt": {
            "type": "string",
            "format": "date-time"
          },
          "signature": {
            "type": "string",
            "description": "Token RFC 3161 de la autoridad de sellado (DER en base64) sobre la huella."
          },
          "signatureFormat": {
            "type": "string",
            "enum": [
              "rfc3161"
            ]
          },
          "tsa": {
            "type": "string"
          },
          "verifyUrl": {
            "type": "string",
            "format": "uri"
          },
          "price": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "number"
              },
              "currency": {
                "type": "string"
              }
            }
          },
          "test": {
            "type": "boolean"
          }
        }
      },
      "InstallmentPayment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "amount": {
            "type": "number"
          },
          "externalReference": {
            "type": [
              "string",
              "null"
            ]
          },
          "receiptSha256": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[0-9a-f]{64}$"
          },
          "registeredAt": {
            "type": "string",
            "format": "date-time"
          },
          "voidedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Installment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "number": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "dueDate": {
            "type": "string",
            "format": "date"
          },
          "amount": {
            "type": "number"
          },
          "currency": {
            "type": "string",
            "enum": [
              "ARS",
              "USD"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "overdue",
              "paid",
              "cancelled"
            ]
          },
          "paidAmount": {
            "type": "number"
          },
          "period": {
            "type": "integer",
            "description": "Período de ajuste: 0 = el primero."
          },
          "adjustment": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "index": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "icl",
                  "ipc",
                  "uva",
                  "cer",
                  "casa_propia",
                  null
                ]
              },
              "formula": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "factor": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "manual": {
                "type": "boolean"
              }
            }
          },
          "payments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InstallmentPayment"
            }
          }
        }
      },
      "InstallmentSchedule": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "contractId": {
            "type": "string",
            "format": "uuid"
          },
          "currency": {
            "type": [
              "string",
              "null"
            ]
          },
          "installments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Installment"
            }
          },
          "nextAdjustment": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "date": {
                "type": "string",
                "format": "date"
              },
              "index": {
                "type": "string",
                "enum": [
                  "icl",
                  "ipc",
                  "uva",
                  "cer",
                  "casa_propia"
                ]
              },
              "period": {
                "type": "integer"
              },
              "waitingIndex": {
                "type": "boolean",
                "description": "El índice no salió en 3 días (o es Casa Propia): se carga a mano con POST /adjustments."
              }
            }
          }
        }
      }
    }
  }
}
