{
  "openapi": "3.0.3",
  "info": {
    "title": "Apifact API",
    "version": "2.0.0-alpha.27",
    "description": "Canonical multi-tenant API. Document creation validates and persists a document; it does not submit it to SUNAT."
  },
  "servers": [
    {
      "url": "https://apifact.fac-360.com",
      "description": "Production. Every accepted document here is a real comprobante: it enters the taxpayer's sales ledger and can only be withdrawn by an accepted comunicacion de baja. Requires a credential minted on this edge; a staging one answers 401."
    },
    {
      "url": "https://apifact-staging.fac-360.com",
      "description": "Staging. Issues against SUNAT beta, so nothing sent through it is fiscally valid and nothing costs anything. This is where an integration should be built and where the certificate activation probe is free."
    }
  ],
  "tags": [
    {
      "name": "System"
    },
    {
      "name": "Documents"
    },
    {
      "name": "Summaries"
    },
    {
      "name": "Payment regimes"
    },
    {
      "name": "Artifacts"
    },
    {
      "name": "Operations"
    },
    {
      "name": "Webhooks"
    },
    {
      "name": "Certificates"
    },
    {
      "name": "Organizations"
    },
    {
      "name": "Consultations"
    },
    {
      "name": "Legacy compatibility"
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "System"
        ],
        "operationId": "getHealth",
        "responses": {
          "200": {
            "description": "Service health",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "status",
                    "phase",
                    "environment",
                    "legacyV1AuthConfigured"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "ok"
                      ]
                    },
                    "phase": {
                      "type": "string",
                      "enum": [
                        "phase2"
                      ]
                    },
                    "environment": {
                      "type": "string"
                    },
                    "legacyV1AuthConfigured": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/openapi.json": {
      "get": {
        "tags": [
          "System"
        ],
        "operationId": "getOpenApiV2",
        "responses": {
          "200": {
            "description": "Bundled OpenAPI 3.0.3 document"
          }
        }
      }
    },
    "/api/v1/invoice/create": {
      "post": {
        "tags": [
          "Legacy compatibility"
        ],
        "operationId": "createLegacyInvoice",
        "summary": "Validate and store a legacy factura or boleta payload",
        "deprecated": true,
        "description": "Safe asynchronous compatibility route. Requires documents:create; unsupported legacy fields are rejected.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:create",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A deterministic document-identity key is derived when omitted.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[!-~]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LegacyInvoiceCreateRequest"
              },
              "example": {
                "comprobante": {
                  "tipoOperacion": "0101",
                  "tipoDoc": "01",
                  "serie": "F001",
                  "correlativo": "1030",
                  "fechaEmision": "2026-08-12",
                  "horaEmision": "09:30:00",
                  "tipoMoneda": "PEN",
                  "tipoPago": "Contado"
                },
                "cliente": {
                  "tipoDoc": "6",
                  "numDoc": "20512345678",
                  "rznSocial": "COMERCIAL LOS ANDES S.A.C.",
                  "codigoPais": "PE",
                  "direccion": "AV. AREQUIPA 4321"
                },
                "items": [
                  {
                    "codProducto": "SERV-001",
                    "descripcion": "Consultoria de implementacion - plan mensual",
                    "unidad": "ZZ",
                    "cantidad": "1",
                    "mtoValorUnitario": "1000.00",
                    "codeAfect": "10",
                    "igvPorcent": "18"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay or operation status poll",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyInvoiceResponse"
                }
              }
            }
          },
          "201": {
            "description": "Document created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyInvoiceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/invoice/send": {
      "post": {
        "tags": [
          "Legacy compatibility"
        ],
        "operationId": "submitLegacyInvoice",
        "summary": "Durably request SUNAT submission for a legacy document identity",
        "deprecated": true,
        "description": "Safe asynchronous compatibility route. Requires documents:submit; unsupported legacy fields are rejected.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:submit",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A deterministic document-identity key is derived when omitted.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[!-~]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LegacyInvoiceReferenceRequest"
              },
              "example": {
                "comprobante": {
                  "tipoDoc": "01",
                  "serie": "F001",
                  "correlativo": "1030"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay or operation status poll",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyInvoiceResponse"
                }
              }
            }
          },
          "202": {
            "description": "Operation accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyInvoiceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/invoice/a4": {
      "post": {
        "tags": [
          "Legacy compatibility"
        ],
        "operationId": "generateLegacyInvoiceA4",
        "summary": "Durably request an immutable A4 representation",
        "deprecated": true,
        "description": "Safe asynchronous compatibility route. Requires documents:pdf; unsupported legacy fields are rejected.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:pdf",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A deterministic document-identity key is derived when omitted.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[!-~]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LegacyInvoiceReferenceRequest"
              },
              "example": {
                "comprobante": {
                  "tipoDoc": "01",
                  "serie": "F001",
                  "correlativo": "1030"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay or operation status poll",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyInvoiceResponse"
                }
              }
            }
          },
          "202": {
            "description": "Operation accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyInvoiceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/invoice/ticket": {
      "post": {
        "tags": [
          "Legacy compatibility"
        ],
        "operationId": "generateLegacyInvoiceTicket",
        "summary": "Durably request an immutable ticket representation",
        "deprecated": true,
        "description": "Safe asynchronous compatibility route. Requires documents:pdf; unsupported legacy fields are rejected.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:pdf",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A deterministic document-identity key is derived when omitted.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[!-~]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LegacyInvoiceReferenceRequest"
              },
              "example": {
                "comprobante": {
                  "tipoDoc": "01",
                  "serie": "F001",
                  "correlativo": "1030"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay or operation status poll",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyInvoiceResponse"
                }
              }
            }
          },
          "202": {
            "description": "Operation accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyInvoiceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Legacy compatibility error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/documents": {
      "post": {
        "tags": [
          "Documents"
        ],
        "operationId": "createDocument",
        "summary": "Validate and durably create a canonical invoice, receipt or fiscal note",
        "description": "Rehearse a body against POST /api/v2/documents/validations first: it runs every check this route runs, answers 200 with the verdict and the computed totals, creates nothing and costs nothing. NEW: 422 DOCUMENT_ARITHMETIC_INVALID refuses the four arithmetic conditions no schema can express, because each needs the document totals — a line whose allowances exceed its own value, a global allowance exceeding the document total, an adjustment over a zero base, and a Credito schedule whose instalments do not settle what is pending (payableAmount, or the detraction's netPayableAmount under the SPOT). Those four were previously accepted here with 201 and failed for the first time during submission, on a comprobante that could no longer be withdrawn. Credit/debit note creation is capability-gated per tenant. Submission uses separate default-off capabilities and, once accepted or observed, produces the QR and the A4/ticket representations for every supported document type. On-demand versioned generation through POST /api/v2/documents/{id}/pdfs is still limited to 01 and 03. The series F000 and B000 are reserved for the platform and are refused with 422 INVALID_DOCUMENT on every document type, notes included: F000 is what the certificate activation probe issues on, and although that probe is not a document of this tenant's, SUNAT records it under this tenant's RUC — so a tenant-issued F000 would collide with a number SUNAT already holds. B000 is reserved beside it although nothing issues on it, because reserving the factura series and leaving its boleta twin free invites the next caller to reach for B000 as the obviously unused one.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:create",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDocumentRequest"
              },
              "examples": {
                "facturaGravada": {
                  "summary": "Factura 01 gravada, la venta domestica ordinaria",
                  "value": {
                    "schemaVersion": "1.0",
                    "documentType": "01",
                    "series": "F001",
                    "number": "1024",
                    "issueDate": "2026-08-12",
                    "issueTime": "10:15:00",
                    "currency": "PEN",
                    "supplier": {
                      "documentType": "6",
                      "documentNumber": "20601030405",
                      "legalName": "MI EMPRESA EMISORA S.A.C.",
                      "address": "AV. JAVIER PRADO ESTE 1234",
                      "ubigeo": "150131",
                      "district": "SAN ISIDRO",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "customer": {
                      "documentType": "6",
                      "documentNumber": "20512345678",
                      "legalName": "COMERCIAL LOS ANDES S.A.C.",
                      "address": "AV. AREQUIPA 4321",
                      "ubigeo": "150122",
                      "district": "MIRAFLORES",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "lines": [
                      {
                        "productCode": "SERV-001",
                        "description": "Consultoria de implementacion - plan mensual",
                        "unitCode": "ZZ",
                        "quantity": "1",
                        "unitValue": "1000.00",
                        "affectationCode": "10"
                      }
                    ]
                  }
                },
                "boletaConDni": {
                  "summary": "Boleta 03 con adquirente identificado por DNI (catalogo 06 1)",
                  "value": {
                    "schemaVersion": "1.0",
                    "documentType": "03",
                    "series": "B001",
                    "number": "3310",
                    "issueDate": "2026-08-12",
                    "issueTime": "18:40:00",
                    "currency": "PEN",
                    "supplier": {
                      "documentType": "6",
                      "documentNumber": "20601030405",
                      "legalName": "MI EMPRESA EMISORA S.A.C.",
                      "address": "AV. JAVIER PRADO ESTE 1234",
                      "ubigeo": "150131",
                      "district": "SAN ISIDRO",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "customer": {
                      "documentType": "1",
                      "documentNumber": "45678912",
                      "legalName": "MARIA FERNANDA QUISPE ROJAS"
                    },
                    "lines": [
                      {
                        "productCode": "PROD-001",
                        "description": "Polo de algodon talla M",
                        "unitCode": "NIU",
                        "quantity": "2",
                        "unitValue": "25.00",
                        "affectationCode": "10"
                      }
                    ]
                  }
                },
                "boletaAnonima": {
                  "summary": "Boleta 03 anonima: catalogo 06 0 con documentNumber '-'",
                  "description": "La venta de mostrador que no identifica a nadie, legal hasta un total de PEN 700.00 (SUNAT 2514). Es el perfil que menos se adivina y el que el documento publicado llego a declarar imposible.",
                  "value": {
                    "schemaVersion": "1.0",
                    "documentType": "03",
                    "series": "B001",
                    "number": "3311",
                    "issueDate": "2026-08-12",
                    "issueTime": "18:40:00",
                    "currency": "PEN",
                    "supplier": {
                      "documentType": "6",
                      "documentNumber": "20601030405",
                      "legalName": "MI EMPRESA EMISORA S.A.C.",
                      "address": "AV. JAVIER PRADO ESTE 1234",
                      "ubigeo": "150131",
                      "district": "SAN ISIDRO",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "customer": {
                      "documentType": "0",
                      "documentNumber": "-",
                      "legalName": "CLIENTES VARIOS"
                    },
                    "lines": [
                      {
                        "productCode": "PROD-001",
                        "description": "Polo de algodon talla M",
                        "unitCode": "NIU",
                        "quantity": "2",
                        "unitValue": "25.00",
                        "affectationCode": "10"
                      }
                    ]
                  }
                },
                "notaCreditoTotal": {
                  "summary": "Nota de credito 07 catalogo 09 01: anula la factura entera",
                  "description": "Los codigos 01, 02, 03 y 06 anulan: reproducen las lineas y los totales del comprobante afectado, y solo se admite una por factura.",
                  "value": {
                    "schemaVersion": "1.0",
                    "documentType": "07",
                    "series": "FC01",
                    "number": "18",
                    "issueDate": "2026-08-12",
                    "currency": "PEN",
                    "reasonCode": "01",
                    "affectedDocument": {
                      "documentType": "01",
                      "series": "F001",
                      "number": "1025"
                    },
                    "supplier": {
                      "documentType": "6",
                      "documentNumber": "20601030405",
                      "legalName": "MI EMPRESA EMISORA S.A.C.",
                      "address": "AV. JAVIER PRADO ESTE 1234",
                      "ubigeo": "150131",
                      "district": "SAN ISIDRO",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "customer": {
                      "documentType": "6",
                      "documentNumber": "20512345678",
                      "legalName": "COMERCIAL LOS ANDES S.A.C.",
                      "address": "AV. AREQUIPA 4321",
                      "ubigeo": "150122",
                      "district": "MIRAFLORES",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "lines": [
                      {
                        "productCode": "SERV-001",
                        "description": "Consultoria de implementacion - plan mensual",
                        "unitCode": "ZZ",
                        "quantity": "1",
                        "unitValue": "1000.00",
                        "affectationCode": "10"
                      }
                    ]
                  }
                },
                "notaCreditoParcial": {
                  "summary": "Nota de credito 07 catalogo 09 09: acredita parte de la factura",
                  "description": "Los codigos 04, 05, 07, 08 y 09 llevan lineas propias. El credito acumulado contra una factura no puede superar su total: la nota que lo sobrepasa se rechaza con 409 AFFECTED_FACTURA_CREDIT_EXCEEDED.",
                  "value": {
                    "schemaVersion": "1.0",
                    "documentType": "07",
                    "series": "FC01",
                    "number": "19",
                    "issueDate": "2026-08-12",
                    "currency": "PEN",
                    "reasonCode": "09",
                    "affectedDocument": {
                      "documentType": "01",
                      "series": "F001",
                      "number": "1026"
                    },
                    "supplier": {
                      "documentType": "6",
                      "documentNumber": "20601030405",
                      "legalName": "MI EMPRESA EMISORA S.A.C.",
                      "address": "AV. JAVIER PRADO ESTE 1234",
                      "ubigeo": "150131",
                      "district": "SAN ISIDRO",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "customer": {
                      "documentType": "6",
                      "documentNumber": "20512345678",
                      "legalName": "COMERCIAL LOS ANDES S.A.C.",
                      "address": "AV. AREQUIPA 4321",
                      "ubigeo": "150122",
                      "district": "MIRAFLORES",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "lines": [
                      {
                        "productCode": "SERV-001",
                        "description": "Disminucion en el valor - descuento comercial acordado",
                        "unitCode": "ZZ",
                        "quantity": "1",
                        "unitValue": "200.00",
                        "affectationCode": "10"
                      }
                    ]
                  }
                },
                "notaCreditoSobreBoleta": {
                  "summary": "Nota de credito 07 sobre una boleta 03: serie B, adquirente de la boleta",
                  "value": {
                    "schemaVersion": "1.0",
                    "documentType": "07",
                    "series": "BC01",
                    "number": "4",
                    "issueDate": "2026-08-12",
                    "currency": "PEN",
                    "reasonCode": "01",
                    "affectedDocument": {
                      "documentType": "03",
                      "series": "B001",
                      "number": "3310"
                    },
                    "supplier": {
                      "documentType": "6",
                      "documentNumber": "20601030405",
                      "legalName": "MI EMPRESA EMISORA S.A.C.",
                      "address": "AV. JAVIER PRADO ESTE 1234",
                      "ubigeo": "150131",
                      "district": "SAN ISIDRO",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "customer": {
                      "documentType": "1",
                      "documentNumber": "45678912",
                      "legalName": "MARIA FERNANDA QUISPE ROJAS"
                    },
                    "lines": [
                      {
                        "productCode": "PROD-001",
                        "description": "Polo de algodon talla M",
                        "unitCode": "NIU",
                        "quantity": "2",
                        "unitValue": "25.00",
                        "affectationCode": "10"
                      }
                    ]
                  }
                },
                "notaDebito": {
                  "summary": "Nota de debito 08 catalogo 10 03",
                  "value": {
                    "schemaVersion": "1.0",
                    "documentType": "08",
                    "series": "FD01",
                    "number": "7",
                    "issueDate": "2026-08-12",
                    "currency": "PEN",
                    "reasonCode": "03",
                    "affectedDocument": {
                      "documentType": "01",
                      "series": "F001",
                      "number": "1026"
                    },
                    "supplier": {
                      "documentType": "6",
                      "documentNumber": "20601030405",
                      "legalName": "MI EMPRESA EMISORA S.A.C.",
                      "address": "AV. JAVIER PRADO ESTE 1234",
                      "ubigeo": "150131",
                      "district": "SAN ISIDRO",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "customer": {
                      "documentType": "6",
                      "documentNumber": "20512345678",
                      "legalName": "COMERCIAL LOS ANDES S.A.C.",
                      "address": "AV. AREQUIPA 4321",
                      "ubigeo": "150122",
                      "district": "MIRAFLORES",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "lines": [
                      {
                        "productCode": "PEN-001",
                        "description": "Penalidad por incumplimiento de plazo contractual",
                        "unitCode": "ZZ",
                        "quantity": "1",
                        "unitValue": "100.00",
                        "affectationCode": "10"
                      }
                    ]
                  }
                },
                "facturaConDetraccion": {
                  "summary": "Factura con detraccion (SPOT), con su operationType 1001",
                  "value": {
                    "schemaVersion": "1.0",
                    "documentType": "01",
                    "series": "F001",
                    "number": "1027",
                    "issueDate": "2026-08-12",
                    "issueTime": "10:15:00",
                    "currency": "PEN",
                    "supplier": {
                      "documentType": "6",
                      "documentNumber": "20601030405",
                      "legalName": "MI EMPRESA EMISORA S.A.C.",
                      "address": "AV. JAVIER PRADO ESTE 1234",
                      "ubigeo": "150131",
                      "district": "SAN ISIDRO",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "customer": {
                      "documentType": "6",
                      "documentNumber": "20512345678",
                      "legalName": "COMERCIAL LOS ANDES S.A.C.",
                      "address": "AV. AREQUIPA 4321",
                      "ubigeo": "150122",
                      "district": "MIRAFLORES",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "lines": [
                      {
                        "productCode": "TRA-001",
                        "description": "Servicio de transporte de carga Lima - Arequipa",
                        "unitCode": "ZZ",
                        "quantity": "1",
                        "unitValue": "5000.00",
                        "affectationCode": "10"
                      }
                    ],
                    "operationType": "1001",
                    "detraction": {
                      "code": "027",
                      "percent": "4",
                      "supplierAccount": "00065012345",
                      "paymentMeansCode": "001"
                    }
                  }
                },
                "facturaAlCredito": {
                  "summary": "Factura al credito: paymentMethod y el calendario de cuotas",
                  "value": {
                    "schemaVersion": "1.0",
                    "documentType": "01",
                    "series": "F001",
                    "number": "1028",
                    "issueDate": "2026-08-12",
                    "issueTime": "10:15:00",
                    "currency": "PEN",
                    "supplier": {
                      "documentType": "6",
                      "documentNumber": "20601030405",
                      "legalName": "MI EMPRESA EMISORA S.A.C.",
                      "address": "AV. JAVIER PRADO ESTE 1234",
                      "ubigeo": "150131",
                      "district": "SAN ISIDRO",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "customer": {
                      "documentType": "6",
                      "documentNumber": "20512345678",
                      "legalName": "COMERCIAL LOS ANDES S.A.C.",
                      "address": "AV. AREQUIPA 4321",
                      "ubigeo": "150122",
                      "district": "MIRAFLORES",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "lines": [
                      {
                        "productCode": "SERV-001",
                        "description": "Consultoria de implementacion - plan mensual",
                        "unitCode": "ZZ",
                        "quantity": "1",
                        "unitValue": "1000.00",
                        "affectationCode": "10"
                      }
                    ],
                    "paymentMethod": "Credito",
                    "installments": [
                      {
                        "amount": "590.00",
                        "dueDate": "2026-09-11"
                      },
                      {
                        "amount": "590.00",
                        "dueDate": "2026-10-11"
                      }
                    ]
                  }
                },
                "facturaExportacion": {
                  "summary": "Exportacion: afectacion 40, adquirente no domiciliado y USD",
                  "value": {
                    "schemaVersion": "1.0",
                    "documentType": "01",
                    "series": "F001",
                    "number": "1029",
                    "issueDate": "2026-08-12",
                    "currency": "USD",
                    "operationType": "0200",
                    "supplier": {
                      "documentType": "6",
                      "documentNumber": "20601030405",
                      "legalName": "MI EMPRESA EMISORA S.A.C.",
                      "address": "AV. JAVIER PRADO ESTE 1234",
                      "ubigeo": "150131",
                      "district": "SAN ISIDRO",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "customer": {
                      "documentType": "7",
                      "documentNumber": "AB1234567",
                      "legalName": "GLOBAL TRADING LLC",
                      "countryCode": "US",
                      "address": "1200 BRICKELL AVE, MIAMI FL"
                    },
                    "lines": [
                      {
                        "productCode": "EXP-001",
                        "description": "Cafe verde arabica en sacos de 60 kg",
                        "unitCode": "KGM",
                        "quantity": "600",
                        "unitValue": "4.50",
                        "affectationCode": "40"
                      }
                    ]
                  }
                },
                "boletaConIcbper": {
                  "summary": "Boleta con ICBPER: el impuesto va en la linea que lo soporta",
                  "value": {
                    "schemaVersion": "1.0",
                    "documentType": "03",
                    "series": "B001",
                    "number": "3312",
                    "issueDate": "2026-08-12",
                    "issueTime": "18:40:00",
                    "currency": "PEN",
                    "supplier": {
                      "documentType": "6",
                      "documentNumber": "20601030405",
                      "legalName": "MI EMPRESA EMISORA S.A.C.",
                      "address": "AV. JAVIER PRADO ESTE 1234",
                      "ubigeo": "150131",
                      "district": "SAN ISIDRO",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "customer": {
                      "documentType": "1",
                      "documentNumber": "45678912",
                      "legalName": "MARIA FERNANDA QUISPE ROJAS"
                    },
                    "lines": [
                      {
                        "productCode": "PROD-001",
                        "description": "Polo de algodon talla M",
                        "unitCode": "NIU",
                        "quantity": "2",
                        "unitValue": "25.00",
                        "affectationCode": "10"
                      },
                      {
                        "productCode": "BOL-001",
                        "description": "Bolsa plastica",
                        "unitCode": "NIU",
                        "quantity": "3",
                        "unitValue": "0.10",
                        "affectationCode": "10",
                        "icbperUnitAmount": "0.50"
                      }
                    ]
                  }
                },
                "facturaConTransferenciaGratuita": {
                  "summary": "Transferencia gratuita: afectacion 15 con igvRate explicito",
                  "value": {
                    "schemaVersion": "1.0",
                    "documentType": "01",
                    "series": "F001",
                    "number": "1031",
                    "issueDate": "2026-08-12",
                    "issueTime": "10:15:00",
                    "currency": "PEN",
                    "supplier": {
                      "documentType": "6",
                      "documentNumber": "20601030405",
                      "legalName": "MI EMPRESA EMISORA S.A.C.",
                      "address": "AV. JAVIER PRADO ESTE 1234",
                      "ubigeo": "150131",
                      "district": "SAN ISIDRO",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "customer": {
                      "documentType": "6",
                      "documentNumber": "20512345678",
                      "legalName": "COMERCIAL LOS ANDES S.A.C.",
                      "address": "AV. AREQUIPA 4321",
                      "ubigeo": "150122",
                      "district": "MIRAFLORES",
                      "province": "LIMA",
                      "department": "LIMA"
                    },
                    "lines": [
                      {
                        "productCode": "SERV-001",
                        "description": "Consultoria de implementacion - plan mensual",
                        "unitCode": "ZZ",
                        "quantity": "1",
                        "unitValue": "1000.00",
                        "affectationCode": "10"
                      },
                      {
                        "productCode": "BON-001",
                        "description": "Muestra comercial entregada sin cargo",
                        "unitCode": "NIU",
                        "quantity": "1",
                        "unitValue": "80.00",
                        "affectationCode": "15",
                        "igvRate": "18"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentOperationResponse"
                }
              }
            }
          },
          "201": {
            "description": "Document created in VALIDATED state",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentOperationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked or invalid credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks documents:create",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/documents/validations": {
      "post": {
        "tags": [
          "Documents"
        ],
        "operationId": "validateDocument",
        "summary": "Ask what POST /api/v2/documents would answer, without creating anything",
        "description": "The development tool. It runs every check the create route runs and then throws the result away: no comprobante, no correlativo, no operation, no outbox event, no quota. Free, and rate limited to 60 requests per 60 seconds per credential — a limit about how OFTEN you ask, never about what you sent, which is why it is a 429 with an error envelope and never a verdict.\n\nIT CANNOT WRITE. Not 'it does not': the route holds a repository class with no writing method, and that method runs inside a PostgreSQL READ ONLY transaction, so every INSERT, UPDATE and DELETE inside it — including one a trigger would fire — is refused by the server. A dry-run flag on the create route was rejected for the obvious reason: the day somebody leaves it set, a real comprobante exists.\n\nIT ANSWERS 200 EVEN WHEN THE DOCUMENT IS INVALID. The verdict is `valid`, in the body, and the status says only whether the service could answer. That is the opposite of the rest of this API and it is on purpose: this is the one route you call in a loop while programming, where 'invalid' is the expected result, and most HTTP clients turn a 4xx into a thrown exception you would then have to catch and unwrap to read the list you asked for.\n\nfindings[0] IS THE ERROR THE CREATE ROUTE WOULD ANSWER WITH — same code, same status, same details, because both routes read one implementation of the taxonomy. The remaining findings are the problems it would only have reported on your NEXT attempt; collecting them is what removes the round trips. `totals` is present whenever the arithmetic closes, so you can compare the IGV, the payable amount, the detraction split and the derived Cuota identifiers against your own before you issue anything.\n\nNO Idempotency-Key. There is nothing to replay.\n\nTHE TWO THINGS IT DOES NOT ANSWER. It does not tell you whether YOUR CREDENTIAL may create the document: a credential holding only documents:validate is told valid: true here and refused with 403 there, which is the point of the separate scope. And two of the rules it reports are snapshots of shared state — the accumulated credit on the affected comprobante, and whether that comprobante already has a cancellation note — so a note created by another request between this answer and yours moves them. That is a race, not a disagreement.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:validate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDocumentRequest"
              },
              "example": {
                "schemaVersion": "1.0",
                "documentType": "01",
                "series": "F001",
                "number": "1024",
                "issueDate": "2026-08-12",
                "issueTime": "10:15:00",
                "currency": "PEN",
                "supplier": {
                  "documentType": "6",
                  "documentNumber": "20601030405",
                  "legalName": "MI EMPRESA EMISORA S.A.C.",
                  "address": "AV. JAVIER PRADO ESTE 1234",
                  "ubigeo": "150131",
                  "district": "SAN ISIDRO",
                  "province": "LIMA",
                  "department": "LIMA"
                },
                "customer": {
                  "documentType": "6",
                  "documentNumber": "20512345678",
                  "legalName": "COMERCIAL LOS ANDES S.A.C.",
                  "address": "AV. AREQUIPA 4321",
                  "ubigeo": "150122",
                  "district": "MIRAFLORES",
                  "province": "LIMA",
                  "department": "LIMA"
                },
                "lines": [
                  {
                    "productCode": "SERV-001",
                    "description": "Consultoria de implementacion - plan mensual",
                    "unitCode": "ZZ",
                    "quantity": "1",
                    "unitValue": "1000.00",
                    "affectationCode": "10"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The verdict. `valid: true` with an empty `findings` array, or `valid: false` with the problems in the order the create route would hit them. `totals` is present whenever the arithmetic closes — including for a document that is invalid for some other reason, because seeing the number is half the work of building a mapping — and absent only when the schema refused the body outright or the totals do not close.",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentValidationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked or invalid credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks documents:validate. documents:create does not imply it, exactly as it does not imply documents:submit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "The per-credential validation rate limit. This route answers the ordinary error envelope with code VALIDATION_RATE_LIMITED and a Retry-After header, NOT the edge `rate_limit_exceeded` body the write routes publish, so a caller in a loop can tell 'you are asking too often' from 'your comprobante is invalid' without parsing a message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "This deployment has no rate limiter bound (VALIDATION_RATE_LIMITER_UNCONFIGURED). The route fails closed rather than becoming an unmetered free endpoint; nothing else is affected.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/documents/{id}": {
      "get": {
        "tags": [
          "Documents"
        ],
        "operationId": "getDocument",
        "summary": "Get a tenant-owned document and its canonical state",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Canonical document state",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentResponse"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks documents:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/documents/{id}/submissions": {
      "post": {
        "tags": [
          "Documents"
        ],
        "operationId": "requestDocumentSubmission",
        "summary": "Durably request document submission",
        "description": "Creates an idempotent operation and outbox event. Factura, boleta and the restricted 07 and 08 note profiles are admitted only when their independent tenant capabilities are enabled: the capabilities are evaluated when the request arrives, and a request that does not meet them is REFUSED with 409 TENANT_CAPABILITY_DISABLED naming the exact capability column, never accepted and held. A 202 therefore means the submission is queued for the private SUNAT Workflow and will reach a terminal state; it never means the request is waiting for a capability to be turned on. A note profile is pinned per type and not per reason, so a 07 that voids its factura and a 07 that credits part of it are dispatched on identical terms. Successful admission does not imply SUNAT acceptance. An accepted or observed submission of any of these document types produces signed XML, submission ZIP, CDR, QR PNG and the A4 and ticket representations; a note prints its own series-number and amounts and names the document it modifies with its catalog 09/10 reason.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:submit",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Idempotent replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentOperationResponse"
                }
              }
            }
          },
          "202": {
            "description": "Submission request durably accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentOperationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks documents:submit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "TENANT_CAPABILITY_DISABLED: a capability this company must have for this document type is off, and error.details lists every requirement of the act with its own verdict, each named by its exact PostgreSQL column — submission_publication_enabled for any type, plus boleta_submission_enabled, credit_note_submission_enabled or debit_note_submission_enabled for 03, 07 and 08. An administrator of the company enables it and the same request then succeeds; GET /api/v2/organizations/companies/{id} reports the current state of all nine. DOCUMENT_TYPE_NOT_SUBMITTABLE, DOCUMENT_NOT_SUBMITTABLE and SUBMISSION_ALREADY_PENDING are the other three: the type cannot be sent on this route, the document is not in a submittable state, or a submission of it is already in flight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/documents/{id}/pdfs": {
      "post": {
        "tags": [
          "Artifacts"
        ],
        "operationId": "requestPdfGeneration",
        "summary": "Durably request immutable PDF representations",
        "description": "Pins canonical and signed-XML sources plus explicit template/renderer versions. Generation is asynchronous and never changes fiscal document state. Admission is limited to factura 01 and boleta 03; a 07 or 08 note obtains its QR and representations from its own accepted or observed submission.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:pdf",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GeneratePdfRequest"
              },
              "example": {
                "schemaVersion": "2.0",
                "outputs": [
                  {
                    "kind": "pdf-a4",
                    "templateVersion": "invoice-a4-v2"
                  },
                  {
                    "kind": "pdf-ticket",
                    "templateVersion": "invoice-ticket-v1"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PdfGenerationResponse"
                }
              }
            }
          },
          "202": {
            "description": "PDF generation durably accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PdfGenerationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks documents:pdf",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/documents/{id}/artifacts": {
      "get": {
        "tags": [
          "Artifacts"
        ],
        "operationId": "listDocumentArtifacts",
        "summary": "List public artifacts for a tenant-owned document",
        "description": "Returns safe metadata only. Internal object keys, HTML render inputs and unsigned XML are never exposed.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "artifacts:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "representationLimit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "representationCursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 256
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Public artifact metadata",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ArtifactListResponse"
                    },
                    {
                      "$ref": "#/components/schemas/PaginatedArtifactListResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks artifacts:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/documents/{id}/artifacts/{kind}": {
      "get": {
        "tags": [
          "Artifacts"
        ],
        "operationId": "downloadDocumentArtifact",
        "summary": "Stream and integrity-check a public document artifact",
        "description": "Streams immutable bytes through a private R2-only service after tenant authorization and metadata integrity checks. Range requests are not supported.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "artifacts:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "kind",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "signed-xml",
                "submission-zip",
                "cdr-zip",
                "cdr-xml",
                "pdf-a4",
                "pdf-ticket",
                "qr-png"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Integrity-checked artifact stream",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "private, no-store"
                  ]
                }
              },
              "Content-Digest": {
                "schema": {
                  "type": "string"
                }
              },
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                }
              },
              "Content-Length": {
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks artifacts:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/documents/{id}/artifacts/versions/{representationId}/{kind}": {
      "get": {
        "tags": [
          "Artifacts"
        ],
        "operationId": "downloadPdfRepresentation",
        "summary": "Download an immutable versioned PDF representation",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "artifacts:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "representationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "kind",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "pdf-a4",
                "pdf-ticket"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Integrity-checked immutable PDF stream",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks artifacts:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/summaries": {
      "post": {
        "tags": [
          "Summaries"
        ],
        "operationId": "createSummary",
        "summary": "Validate and durably create a daily summary (RC)",
        "description": "A daily summary (RC) reports the boletas issued on one day and the notes that affect them, and it is the only way to annul a boleta: a line with statusCode 3, never a void communication. Creation is capability-gated per tenant and does not contact SUNAT. A line naming a comprobante this API holds is checked against it: the comprobante must have been accepted or observed by SUNAT, and must not already be withdrawn or carried by another live communication. A line naming a comprobante this API does not hold is accepted and recorded as unlinked — see `references` — because an emitter may have issued it through another provider, on paper, or before integrating; SUNAT's own ERROR 2105 and 2323 police that case.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:create",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSummaryRequest"
              },
              "example": {
                "schemaVersion": "1.0",
                "documentType": "RC",
                "issueDate": "2026-08-12",
                "referenceDate": "2026-08-12",
                "correlative": "1",
                "supplier": {
                  "documentType": "6",
                  "documentNumber": "20601030405",
                  "legalName": "MI EMPRESA EMISORA S.A.C."
                },
                "lines": [
                  {
                    "documentType": "03",
                    "series": "B002",
                    "number": "1",
                    "statusCode": "1",
                    "currency": "PEN",
                    "totalAmount": "59.00",
                    "payments": {
                      "taxable": "50.00"
                    },
                    "taxes": {
                      "igv": "9.00"
                    }
                  },
                  {
                    "documentType": "03",
                    "series": "B002",
                    "number": "2",
                    "statusCode": "1",
                    "currency": "PEN",
                    "customer": {
                      "documentType": "1",
                      "documentNumber": "45678912"
                    },
                    "totalAmount": "826.00",
                    "payments": {
                      "taxable": "700.00"
                    },
                    "taxes": {
                      "igv": "126.00"
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SummaryOperationResponse"
                }
              }
            }
          },
          "201": {
            "description": "daily summary (RC) created in VALIDATED state",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SummaryOperationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked or invalid credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks documents:create",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "A line names a comprobante this API holds that cannot be reported or withdrawn. AFFECTED_DOCUMENT_NOT_REGISTERED: its submission state is neither ACCEPTED nor OBSERVED, so send it to SUNAT first (SUNAT ERROR 2105 / 2398). AFFECTED_DOCUMENT_ALREADY_VOIDED: it was already withdrawn by a communication SUNAT accepted, which is permanent and has no remedy (SUNAT ERROR 2323 / 2987). AFFECTED_DOCUMENT_VOID_IN_FLIGHT: another baja holds it, or one ended AMBIGUOUS — wait for that communication to settle, and if it is rejected the comprobante is withdrawable again; never send a second baja. AFFECTED_DOCUMENT_ALREADY_REPORTED: another resumen diario already carries it, so look at that summary (SUNAT ERROR 2282). A line naming a comprobante this API does not hold is never refused here.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/summaries/{id}": {
      "get": {
        "tags": [
          "Summaries"
        ],
        "operationId": "getSummary",
        "summary": "Get a tenant-owned daily summary (RC) and its ticket operation",
        "description": "Reports the durable operation behind the summary, including the SUNAT ticket once it exists. The ticket is the only handle that can resolve a dispatch out of band, so it is published rather than hidden.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Summary state and ticket operation",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SummaryResponse"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks documents:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/summaries/{id}/submissions": {
      "post": {
        "tags": [
          "Summaries"
        ],
        "operationId": "requestSummarySubmission",
        "summary": "Durably request daily summary (RC) dispatch",
        "description": "Builds the SummaryDocuments UBL from the stored canonical payload, signs it, packages it and dispatches it with sendSummary, all inside a private durable Workflow. sendSummary answers with a ticket rather than a CDR, so the operation stays in TICKET_PENDING until getStatus resolves it. A dispatch whose ticket is lost is terminal and AMBIGUOUS: it is never resent automatically.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:submit",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Idempotent replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SummaryOperationResponse"
                }
              }
            }
          },
          "202": {
            "description": "Dispatch request durably accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SummaryOperationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks documents:submit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "The durable dispatch handle could not be started. Nothing reached SUNAT; retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/voided": {
      "post": {
        "tags": [
          "Summaries"
        ],
        "operationId": "createVoidedCommunication",
        "summary": "Validate and durably create a void communication (RA)",
        "description": "A void communication (RA) withdraws already-issued facturas and the notes over them. Boleta 03 is deliberately not accepted: a boleta is annulled through a daily summary line with statusCode 3. Creation is capability-gated per tenant and does not contact SUNAT. The response reports the communication deadline; it never enforces it. A line naming a comprobante this API holds is checked against it: the comprobante must have been accepted or observed by SUNAT, and must not already be withdrawn or carried by another live communication. A line naming a comprobante this API does not hold is accepted and recorded as unlinked — see `references` — because an emitter may have issued it through another provider, on paper, or before integrating; SUNAT's own ERROR 2105 and 2323 police that case.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:create",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateVoidedCommunicationRequest"
              },
              "example": {
                "schemaVersion": "1.0",
                "documentType": "RA",
                "issueDate": "2026-08-12",
                "referenceDate": "2026-08-12",
                "correlative": "1",
                "supplier": {
                  "documentType": "6",
                  "documentNumber": "20601030405",
                  "legalName": "MI EMPRESA EMISORA S.A.C."
                },
                "lines": [
                  {
                    "documentType": "01",
                    "series": "F001",
                    "number": "1024",
                    "reason": "ERROR EN EL IMPORTE UNITARIO"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SummaryOperationResponse"
                }
              }
            }
          },
          "201": {
            "description": "void communication (RA) created in VALIDATED state",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SummaryOperationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked or invalid credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks documents:create",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "A line names a comprobante this API holds that cannot be reported or withdrawn. AFFECTED_DOCUMENT_NOT_REGISTERED: its submission state is neither ACCEPTED nor OBSERVED, so send it to SUNAT first (SUNAT ERROR 2105 / 2398). AFFECTED_DOCUMENT_ALREADY_VOIDED: it was already withdrawn by a communication SUNAT accepted, which is permanent and has no remedy (SUNAT ERROR 2323 / 2987). AFFECTED_DOCUMENT_VOID_IN_FLIGHT: another baja holds it, or one ended AMBIGUOUS — wait for that communication to settle, and if it is rejected the comprobante is withdrawable again; never send a second baja. AFFECTED_DOCUMENT_ALREADY_REPORTED: another resumen diario already carries it, so look at that summary (SUNAT ERROR 2282). A line naming a comprobante this API does not hold is never refused here.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/voided/{id}": {
      "get": {
        "tags": [
          "Summaries"
        ],
        "operationId": "getVoidedCommunication",
        "summary": "Get a tenant-owned void communication (RA) and its ticket operation",
        "description": "Reports the durable operation behind the summary, including the SUNAT ticket once it exists. The ticket is the only handle that can resolve a dispatch out of band, so it is published rather than hidden.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Summary state and ticket operation",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SummaryResponse"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks documents:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/voided/{id}/submissions": {
      "post": {
        "tags": [
          "Summaries"
        ],
        "operationId": "requestVoidedCommunicationSubmission",
        "summary": "Durably request void communication (RA) dispatch",
        "description": "Builds the VoidedDocuments UBL from the stored canonical payload, signs it, packages it and dispatches it with sendSummary inside the same private durable Workflow as the daily summary, with the same ticket cycle and the same non-resendable ambiguity fence.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:submit",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Idempotent replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SummaryOperationResponse"
                }
              }
            }
          },
          "202": {
            "description": "Dispatch request durably accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SummaryOperationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks documents:submit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "The durable dispatch handle could not be started. Nothing reached SUNAT; retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/retentions": {
      "post": {
        "tags": [
          "Payment regimes"
        ],
        "operationId": "createRetention",
        "summary": "Validate and durably create a comprobante de retención (CRE, 20)",
        "description": "A comprobante de retención declares the documents an agente de retención is settling, the payment made against each one, the amount withheld and the net handed over. Every monetary figure in the response is DERIVED from the payments and the declared rate; none may be supplied. Creation is capability-gated per tenant (retention_issuance_enabled, off by default) and does not contact SUNAT.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:create",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateRetentionRequest"
              },
              "example": {
                "schemaVersion": "1.0",
                "documentType": "20",
                "series": "R001",
                "number": "56",
                "issueDate": "2026-08-12",
                "currency": "PEN",
                "regimeCode": "01",
                "regimePercent": "3",
                "note": "Retencion del regimen general aplicada al pago de la factura del proveedor",
                "agent": {
                  "documentType": "6",
                  "documentNumber": "20601030405",
                  "legalName": "MI EMPRESA EMISORA S.A.C.",
                  "address": "AV. JAVIER PRADO ESTE 1234",
                  "ubigeo": "150131",
                  "district": "SAN ISIDRO",
                  "province": "LIMA",
                  "department": "LIMA"
                },
                "receiver": {
                  "documentType": "6",
                  "documentNumber": "20512345678",
                  "legalName": "COMERCIAL LOS ANDES S.A.C.",
                  "address": "AV. AREQUIPA 4321",
                  "ubigeo": "150122",
                  "district": "MIRAFLORES",
                  "province": "LIMA",
                  "department": "LIMA"
                },
                "documents": [
                  {
                    "documentType": "01",
                    "series": "F500",
                    "number": "128",
                    "issueDate": "2026-08-12",
                    "totalAmount": "1180.00",
                    "currency": "PEN",
                    "payment": {
                      "id": "1",
                      "amount": "1180.00",
                      "date": "2026-08-12"
                    },
                    "regimeDate": "2026-08-12"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRegimeOperationResponse"
                }
              }
            }
          },
          "201": {
            "description": "comprobante de retención (CRE, 20) created in VALIDATED state",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRegimeOperationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked or invalid credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "The credential lacks documents:create, or the declared agent is not the authenticated issuer. The agent of a CRE/CPE is its issuer, so declaring another RUC is an authorization failure rather than a payload error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/retentions/{id}": {
      "get": {
        "tags": [
          "Payment regimes"
        ],
        "operationId": "getRetention",
        "summary": "Get a tenant-owned comprobante de retención (CRE, 20) and its references",
        "description": "Returns the stored header and every reference in the order the signed XML declares them, with the PEN base, the amount withheld and the net for each. netAmount = baseAmount - regimeAmount.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Header and references, in declaration order",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRegimeResponse"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks documents:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/retentions/{id}/submissions": {
      "post": {
        "tags": [
          "Payment regimes"
        ],
        "operationId": "submitRetention",
        "summary": "Durably request comprobante de retención (CRE, 20) dispatch",
        "description": "Durably requests dispatch through SUNAT's 'otros CPE' service, which is a different deployment from the one that carries facturas and boletas. Answers 202 with the operation id allocated at creation. Dispatch is gated by submission_publication_enabled AND retention_issuance_enabled, both re-read under a row lock immediately before the outbound call. Exactly one envelope can ever leave for a given document: the attempt is fenced in the database before the call, and a run that finds a fenced attempt with no CDR settles AMBIGUOUS for reconciliation rather than resending.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:submit",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Idempotent replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRegimeOperationResponse"
                }
              }
            }
          },
          "202": {
            "description": "Dispatch request durably accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRegimeOperationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks documents:submit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "The tenant capability is off, a dispatch is already in flight, or the document has already settled. A settled document is never redispatched: an ambiguous submission is fenced for reconciliation, never resent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "The durable dispatch handle could not be started. Nothing reached SUNAT; retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/perceptions": {
      "post": {
        "tags": [
          "Payment regimes"
        ],
        "operationId": "createPerception",
        "summary": "Validate and durably create a comprobante de percepción (CPE, 40)",
        "description": "A comprobante de percepción declares the documents an agente de percepción is collecting, the collection made against each one, the amount charged on top and the total collected. It is the mirror of the retention and the only arithmetic difference is the sign. Creation is capability-gated per tenant (perception_issuance_enabled, off by default) and does not contact SUNAT.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:create",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePerceptionRequest"
              },
              "example": {
                "schemaVersion": "1.0",
                "documentType": "40",
                "series": "P001",
                "number": "31",
                "issueDate": "2026-08-12",
                "currency": "PEN",
                "regimeCode": "01",
                "regimePercent": "2",
                "note": "Percepcion por venta interna aplicada al cobro de la factura",
                "agent": {
                  "documentType": "6",
                  "documentNumber": "20601030405",
                  "legalName": "MI EMPRESA EMISORA S.A.C.",
                  "address": "AV. JAVIER PRADO ESTE 1234",
                  "ubigeo": "150131",
                  "district": "SAN ISIDRO",
                  "province": "LIMA",
                  "department": "LIMA"
                },
                "receiver": {
                  "documentType": "6",
                  "documentNumber": "20512345678",
                  "legalName": "COMERCIAL LOS ANDES S.A.C.",
                  "address": "AV. AREQUIPA 4321",
                  "ubigeo": "150122",
                  "district": "MIRAFLORES",
                  "province": "LIMA",
                  "department": "LIMA"
                },
                "documents": [
                  {
                    "documentType": "01",
                    "series": "F001",
                    "number": "1024",
                    "issueDate": "2026-08-12",
                    "totalAmount": "1180.00",
                    "currency": "PEN",
                    "payment": {
                      "id": "1",
                      "amount": "1180.00",
                      "date": "2026-08-12"
                    },
                    "regimeDate": "2026-08-12"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRegimeOperationResponse"
                }
              }
            }
          },
          "201": {
            "description": "comprobante de percepción (CPE, 40) created in VALIDATED state",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRegimeOperationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, revoked or invalid credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "The credential lacks documents:create, or the declared agent is not the authenticated issuer. The agent of a CRE/CPE is its issuer, so declaring another RUC is an authorization failure rather than a payload error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/perceptions/{id}": {
      "get": {
        "tags": [
          "Payment regimes"
        ],
        "operationId": "getPerception",
        "summary": "Get a tenant-owned comprobante de percepción (CPE, 40) and its references",
        "description": "Returns the stored header and every reference in the order the signed XML declares them, with the PEN base, the amount perceived and the total for each. netAmount = baseAmount + regimeAmount.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Header and references, in declaration order",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRegimeResponse"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks documents:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/perceptions/{id}/submissions": {
      "post": {
        "tags": [
          "Payment regimes"
        ],
        "operationId": "submitPerception",
        "summary": "Durably request comprobante de percepción (CPE, 40) dispatch",
        "description": "The perception mirror of submitRetention: same 'otros CPE' service, same single-envelope fence, same AMBIGUOUS outcome for a dispatch whose CDR never arrived. Gated by submission_publication_enabled AND perception_issuance_enabled.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "documents:submit",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Idempotent replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRegimeOperationResponse"
                }
              }
            }
          },
          "202": {
            "description": "Dispatch request durably accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRegimeOperationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks documents:submit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "The tenant capability is off, a dispatch is already in flight, or the document has already settled. A settled document is never redispatched: an ambiguous submission is fenced for reconciliation, never resent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "The durable dispatch handle could not be started. Nothing reached SUNAT; retry with the same Idempotency-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/webhooks/subscriptions": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "createWebhookSubscription",
        "summary": "Register a signed webhook destination for this tenant",
        "description": "Registers an HTTPS destination and the versioned events it should receive, and mints the HMAC signing secret. The secret is returned EXACTLY ONCE, in this response: the API's database role holds no read privilege on the column that stores it, so it cannot be retrieved later by any route. A replay of the same Idempotency-Key answers 200 with replay=true and no secret. Destinations are admitted by an anti-SSRF gate: https only, default port only, no credentials in the URL, no fragment, and the host must be a public DNS name whose last label is alphabetic, which refuses every IP literal in every base as well as localhost, .internal, .local and cloud metadata names.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "webhooks:manage",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[!-~]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWebhookSubscriptionRequest"
              },
              "example": {
                "url": "https://webhooks.example.com/apifact",
                "events": [
                  "document.validated",
                  "document.submitted",
                  "document.accepted",
                  "document.observed",
                  "document.rejected",
                  "document.ambiguous",
                  "document.failed",
                  "document.pdf_ready"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay; the secret is NOT repeated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscriptionResponse"
                }
              }
            }
          },
          "201": {
            "description": "Subscription created; the signing secret is present exactly here",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscriptionCreatedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks webhooks:manage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Payload invalid, or the destination was refused by the anti-SSRF gate. The error code is INVALID_WEBHOOK_DESTINATION and details[0].reason names the rule that refused it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhookSubscriptions",
        "summary": "List this tenant's webhook subscriptions",
        "description": "Returns every subscription of the authenticated tenant with its destination, subscribed events, status and current secret version. No signing secret is ever included.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "webhooks:manage",
        "responses": {
          "200": {
            "description": "Subscriptions owned by the authenticated tenant",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscriptionListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks webhooks:manage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Payload invalid, or the destination was refused by the anti-SSRF gate. The error code is INVALID_WEBHOOK_DESTINATION and details[0].reason names the rule that refused it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/webhooks/subscriptions/{id}": {
      "patch": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "updateWebhookSubscription",
        "summary": "Change a subscription's destination, events or status",
        "description": "Partial update. status=PAUSED stops the fan-out without deleting the subscription or its secret; paused subscriptions receive no new deliveries and are not selected when an event is published. A new url is revalidated by the same anti-SSRF gate as creation.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "webhooks:manage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[!-~]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateWebhookSubscriptionRequest"
              },
              "example": {
                "status": "PAUSED"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated subscription",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscriptionResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks webhooks:manage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Payload invalid, or the destination was refused by the anti-SSRF gate. The error code is INVALID_WEBHOOK_DESTINATION and details[0].reason names the rule that refused it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "deleteWebhookSubscription",
        "summary": "Delete a subscription and everything still aimed at it",
        "description": "Deletes the subscription, its secret versions and its pending and dead-lettered deliveries. Stored events survive, because an event may still be owed to another subscription.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "webhooks:manage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[!-~]+$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription deleted",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscriptionDeletedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks webhooks:manage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Payload invalid, or the destination was refused by the anti-SSRF gate. The error code is INVALID_WEBHOOK_DESTINATION and details[0].reason names the rule that refused it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/webhooks/subscriptions/{id}/secret-rotations": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "rotateWebhookSecret",
        "summary": "Mint the next signing secret version",
        "description": "Creates the next secret version and switches signing to it immediately; there is no overlap window, so install the new secret before rotating. The new secret is returned exactly once. A replay of the same Idempotency-Key answers 200 with replay=true and no secret; if the value was lost, rotate again with a new key.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "webhooks:manage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[!-~]+$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Idempotent replay; the secret is NOT repeated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSecretRotationResponse"
                }
              }
            }
          },
          "201": {
            "description": "New secret version; the secret is present exactly here",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSecretRotationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks webhooks:manage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Payload invalid, or the destination was refused by the anti-SSRF gate. The error code is INVALID_WEBHOOK_DESTINATION and details[0].reason names the rule that refused it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/certificates": {
      "post": {
        "tags": [
          "Certificates"
        ],
        "operationId": "enrollCertificate",
        "summary": "Enrol a signing certificate and its SOL credentials as a DRAFT version",
        "description": "Uploads the four secrets a Peruvian issuer needs: the PKCS#12 certificate (base64), its password, and the SOL user and password of the SUNAT secondary user. All four together, because a certificate signs the XML and the SOL credentials authenticate sendBill: one without the other cannot submit anything. The upload is validated BEFORE it is stored — the PFX must open with the supplied password, the certificate subject RUC must be this company's RUC, the validity window must cover now, and the declared environment must be the one the company is enrolled for — and only then sealed. The new version is created in DRAFT and signs NOTHING until POST /api/v2/certificates/{id}/activation. The certificate and the passwords are never stored in plaintext, never logged and never returned; only the sealed envelopes reach storage, and only their digests reach the database. A repeated Idempotency-Key answers 200 with replay=true and the version the first call produced, without re-sealing anything; the same key with a different certificate is a 409.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "certificates:manage",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[!-~]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EnrollCertificateRequest"
              },
              "example": {
                "environment": "BETA",
                "pfxBase64": "MIIKlgIBAzCCClwGCSqGSIb3DQEHAaCCCk0EggpJMIIKRTCCBgYGCSqGSIb3DQEHAaCCBfcEggXzMIIF7zCCBesGCyqGSIb3DQEMCgECoIIE",
                "pfxPassword": "la-clave-del-pkcs12",
                "solUser": "MODDATOS",
                "solPassword": "moddatos"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay: the version this key already produced, in the listing shape",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateEnrollmentReplayResponse"
                }
              }
            }
          },
          "201": {
            "description": "Certificate enrolled as a DRAFT version; nothing signs with it yet",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateEnrolledResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks certificates:manage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "COMPANY_NOT_ENROLLABLE when the company is not ACTIVE, CERTIFICATE_VERSION_CONFLICT when another enrolment took the version, CERTIFICATE_NOT_ACTIVATABLE when the version is revoked or expired. CERTIFICATE_ALREADY_PROBED when this version has already been probed, whatever the verdict was: a probe consumes F000-<version> at SUNAT and that number is never reissued, so a version that has been probed once cannot be probed again — enrol a new one. CERTIFICATE_ACTIVATION_IN_FLIGHT when another attempt on this version has not settled yet; poll GET /api/v2/certificates/{id}/activation rather than starting a second probe. CERTIFICATE_FIRST_ISSUE_OCCUPIED when a PRODUCTION version is asked to arm for activation by first issue and the company already holds one that is ACTIVE or already armed: at most one version per company and environment can be either, and this mechanism exists for a company that has none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "INVALID_CERTIFICATE_ENROLLMENT for a malformed body, or CERTIFICATE_REJECTED when a validation refused the upload. details[0].field is one of environment, pfx, pfxPassword, solUser or solPassword and details[0].reason names the rule. No value from the upload or from the certificate is ever echoed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "CERTIFICATE_ENROLLMENT_UNAVAILABLE: the private signing boundary is unreachable. CERTIFICATE_ACTIVATION_UNAVAILABLE: the durable probe could not be started, so nothing was sent to SUNAT and the version is untouched; retry with the same Idempotency-Key, which adopts the attempt already recorded instead of opening a second one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Certificates"
        ],
        "operationId": "listCertificates",
        "summary": "List this tenant's certificate versions with their state and validity",
        "description": "Returns every enrolled version of the authenticated tenant, newest first, with its status (DRAFT, ACTIVE or RETIRED), the certificate's identity and validity window, and currentlyValid, which answers whether the version could sign right now. Object keys and secret references are deliberately absent: they are the internal storage layout of the signing boundary and there is nothing a caller can do with them.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "certificates:manage",
        "responses": {
          "200": {
            "description": "Certificate versions owned by the authenticated tenant",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks certificates:manage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "COMPANY_NOT_ENROLLABLE when the company is not ACTIVE, CERTIFICATE_VERSION_CONFLICT when another enrolment took the version, CERTIFICATE_NOT_ACTIVATABLE when the version is revoked or expired. CERTIFICATE_ALREADY_PROBED when this version has already been probed, whatever the verdict was: a probe consumes F000-<version> at SUNAT and that number is never reissued, so a version that has been probed once cannot be probed again — enrol a new one. CERTIFICATE_ACTIVATION_IN_FLIGHT when another attempt on this version has not settled yet; poll GET /api/v2/certificates/{id}/activation rather than starting a second probe. CERTIFICATE_FIRST_ISSUE_OCCUPIED when a PRODUCTION version is asked to arm for activation by first issue and the company already holds one that is ACTIVE or already armed: at most one version per company and environment can be either, and this mechanism exists for a company that has none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "INVALID_CERTIFICATE_ENROLLMENT for a malformed body, or CERTIFICATE_REJECTED when a validation refused the upload. details[0].field is one of environment, pfx, pfxPassword, solUser or solPassword and details[0].reason names the rule. No value from the upload or from the certificate is ever echoed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "CERTIFICATE_ENROLLMENT_UNAVAILABLE: the private signing boundary is unreachable. CERTIFICATE_ACTIVATION_UNAVAILABLE: the durable probe could not be started, so nothing was sent to SUNAT and the version is untouched; retry with the same Idempotency-Key, which adopts the attempt already recorded instead of opening a second one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/certificates/{id}/activation": {
      "post": {
        "tags": [
          "Certificates"
        ],
        "operationId": "activateCertificate",
        "summary": "Prove a DRAFT version against SUNAT and activate it only if SUNAT accepts",
        "description": "Requests activation of the named DRAFT. It does NOT promote it in the request, in either environment: a version becomes ACTIVE only when SUNAT has accepted a comprobante signed with it. What differs is WHICH comprobante, and the mechanism field in the 202 says which of the two you got.\n\nBETA — mechanism PROBE. This API signs a real factura with the DRAFT certificate and sends it to SUNAT beta; the version becomes ACTIVE only if SUNAT accepts it. A certificate that cannot issue is therefore never activated, which is the whole point — the behaviour before this promoted on request, and the first document the tenant tried to issue was the one that discovered the key was unusable. The probe issues documentType 01 on the reserved series F000 with number equal to the enrolment version, so it is unique per version and never reused. It writes no row in the document collections: it is not a comprobante of the tenant's, it is not listed, it has no artifacts and it consumes no quota. Because a SUNAT round trip can take up to the dispatch timeout, this answers 202 with an operationId and a statusPath rather than blocking; poll GET /api/v2/certificates/{id}/activation for the verdict.\n\nPRODUCTION — mechanism FIRST_ISSUE, and it is a different shape because the probe cannot exist here. A probe in production is a REAL comprobante: it enters the taxpayer's registro de ventas and is withdrawn only by an accepted comunicación de baja. Inventing a sale to prove a key is not this platform's decision to make, and refusing to activate at all — which is what this route did until now — left a taxpayer with a certificate that could never sign and a company that could never send. So nothing is sent. The version is ARMED: its status becomes PENDING_FIRST_ISSUE, it signs your documents from this response onwards, and the FIRST comprobante you issue that SUNAT accepts or observes promotes it to ACTIVE in the same transaction that records that CDR — along with the company capabilities that follow a first activation. Nothing is emitted that you were not going to emit anyway.\n\nIf SUNAT then refuses that first comprobante because of the CERTIFICATE OR THE SOL CREDENTIALS — a wrong password, an unknown or inactive SOL user, no CPE profile, a file whose RUC is not the user's — the version is RETIRED and you enrol another. If SUNAT refuses it for anything else, including every CDR rejection about the CONTENT of the document, the version STAYS ARMED and your next comprobante tries again with the same key. A rejection is a statement about the document, not about the certificate, and retiring a good key over one is worse than leaving it pending.\n\nArming is refused with 409 CERTIFICATE_FIRST_ISSUE_OCCUPIED when the company already has a PRODUCTION version that is ACTIVE or already armed; at most one version per company and environment can be either. That means this mechanism does not renew: it is for a company with no signing certificate at all.\n\nEverything decidable without SUNAT is still refused synchronously and by name in both environments: not a DRAFT, revoked, expired, already probed, another attempt in flight. Idempotency-Key is required: a retry after a lost response adopts what was already recorded with replay=true, and re-sending the same key also repairs an attempt whose row committed but whose probe never started.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "certificates:manage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[!-~]+$"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted and durably committed. Under mechanism PROBE the version is still DRAFT and whatever signs this tenant's documents keeps signing them until SUNAT answers. Under mechanism FIRST_ISSUE the version is PENDING_FIRST_ISSUE and is signing already — the tenant had nothing signing before, which is the only situation in which arming is allowed.",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              },
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "Same value as statusPath, for a client that follows headers."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateActivationAcceptedResponse"
                },
                "examples": {
                  "betaProbe": {
                    "summary": "BETA — a factura on F000 is on its way to SUNAT",
                    "value": {
                      "schemaVersion": "2.0",
                      "requestId": "6f0b1a3e-9c2d-4f51-8f0a-2b7c4d5e6f70",
                      "mechanism": "PROBE",
                      "operationId": "1c9a7f52-3d84-4b16-9d0e-8a4f2c6b1e30",
                      "activationId": "1c9a7f52-3d84-4b16-9d0e-8a4f2c6b1e30",
                      "id": "b2d4f6a8-1c3e-4a5b-8d7f-9e0a1b2c3d4e",
                      "resourceId": "b2d4f6a8-1c3e-4a5b-8d7f-9e0a1b2c3d4e",
                      "version": 3,
                      "environment": "BETA",
                      "status": "PENDING",
                      "certificateStatus": "DRAFT",
                      "probe": {
                        "documentType": "01",
                        "series": "F000",
                        "number": "3"
                      },
                      "statusPath": "/api/v2/certificates/b2d4f6a8-1c3e-4a5b-8d7f-9e0a1b2c3d4e/activation",
                      "createdAt": "2026-08-14T15:04:05.000Z",
                      "replay": false
                    }
                  },
                  "productionFirstIssue": {
                    "summary": "PRODUCTION — armed, signing already, promoted by the next accepted comprobante",
                    "description": "Nothing was sent to SUNAT and there is no verdict coming, which is why operationId, activationId and probe are all null. Do not poll statusPath waiting for it to move: the next thing that moves this version is a comprobante you issue. Note certificateStatus: the key IS in use from here, and it is not ACTIVE yet.",
                    "value": {
                      "schemaVersion": "2.0",
                      "requestId": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
                      "mechanism": "FIRST_ISSUE",
                      "operationId": null,
                      "activationId": null,
                      "id": "5f4e3d2c-1b0a-4998-8776-655443322110",
                      "resourceId": "5f4e3d2c-1b0a-4998-8776-655443322110",
                      "version": 1,
                      "environment": "PRODUCTION",
                      "status": "PENDING_FIRST_ISSUE",
                      "certificateStatus": "PENDING_FIRST_ISSUE",
                      "probe": null,
                      "statusPath": "/api/v2/certificates/5f4e3d2c-1b0a-4998-8776-655443322110/activation",
                      "createdAt": "2026-08-14T15:04:05.000Z",
                      "replay": false
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks certificates:manage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "COMPANY_NOT_ENROLLABLE when the company is not ACTIVE, CERTIFICATE_VERSION_CONFLICT when another enrolment took the version, CERTIFICATE_NOT_ACTIVATABLE when the version is revoked or expired. CERTIFICATE_ALREADY_PROBED when this version has already been probed, whatever the verdict was: a probe consumes F000-<version> at SUNAT and that number is never reissued, so a version that has been probed once cannot be probed again — enrol a new one. CERTIFICATE_ACTIVATION_IN_FLIGHT when another attempt on this version has not settled yet; poll GET /api/v2/certificates/{id}/activation rather than starting a second probe. CERTIFICATE_FIRST_ISSUE_OCCUPIED when a PRODUCTION version is asked to arm for activation by first issue and the company already holds one that is ACTIVE or already armed: at most one version per company and environment can be either, and this mechanism exists for a company that has none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "INVALID_CERTIFICATE_ENROLLMENT for a malformed body, or CERTIFICATE_REJECTED when a validation refused the upload. details[0].field is one of environment, pfx, pfxPassword, solUser or solPassword and details[0].reason names the rule. No value from the upload or from the certificate is ever echoed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "CERTIFICATE_ENROLLMENT_UNAVAILABLE: the private signing boundary is unreachable. CERTIFICATE_ACTIVATION_UNAVAILABLE: the durable probe could not be started, so nothing was sent to SUNAT and the version is untouched; retry with the same Idempotency-Key, which adopts the attempt already recorded instead of opening a second one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Certificates"
        ],
        "operationId": "getCertificateActivation",
        "summary": "Read the verdict of this version's activation probe",
        "description": "The poll target the 202 names. Reports the probe's own status, the certificate's status beside it — they are two different questions, and a probe that ended REJECTED leaves a version that is still DRAFT — and the SUNAT verdict verbatim, because a responseCode is what tells an operator which certificate to replace where 'activation failed' tells them nothing. Requires no Idempotency-Key: it is a read. A version that never requested activation answers 404 CERTIFICATE_ACTIVATION_NOT_FOUND, which is not the same as an unknown id: the version exists and simply has no attempt against it.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "certificates:manage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The latest activation attempt recorded for this version",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateActivationStatusResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks certificates:manage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "COMPANY_NOT_ENROLLABLE when the company is not ACTIVE, CERTIFICATE_VERSION_CONFLICT when another enrolment took the version, CERTIFICATE_NOT_ACTIVATABLE when the version is revoked or expired. CERTIFICATE_ALREADY_PROBED when this version has already been probed, whatever the verdict was: a probe consumes F000-<version> at SUNAT and that number is never reissued, so a version that has been probed once cannot be probed again — enrol a new one. CERTIFICATE_ACTIVATION_IN_FLIGHT when another attempt on this version has not settled yet; poll GET /api/v2/certificates/{id}/activation rather than starting a second probe. CERTIFICATE_FIRST_ISSUE_OCCUPIED when a PRODUCTION version is asked to arm for activation by first issue and the company already holds one that is ACTIVE or already armed: at most one version per company and environment can be either, and this mechanism exists for a company that has none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "INVALID_CERTIFICATE_ENROLLMENT for a malformed body, or CERTIFICATE_REJECTED when a validation refused the upload. details[0].field is one of environment, pfx, pfxPassword, solUser or solPassword and details[0].reason names the rule. No value from the upload or from the certificate is ever echoed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "CERTIFICATE_ENROLLMENT_UNAVAILABLE: the private signing boundary is unreachable. CERTIFICATE_ACTIVATION_UNAVAILABLE: the durable probe could not be started, so nothing was sent to SUNAT and the version is untouched; retry with the same Idempotency-Key, which adopts the attempt already recorded instead of opening a second one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/organizations/companies": {
      "get": {
        "tags": [
          "Organizations"
        ],
        "operationId": "listOrganizationCompanies",
        "summary": "List the client companies of the calling organization",
        "description": "The read that makes this surface usable: every company, with the nine capability flags that decide what it may do and the state of the certificate that decides whether it may sign at all. Keyset paginated on the RUC, which is unique platform-wide, so a page cannot skip or repeat a company because another was registered mid-scan. INACTIVE companies are not listed. Requires organizations:read.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "organizations:read",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The RUC the previous page ended on; this page starts strictly after it.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9]{11}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page of client companies with their capability and certificate state",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationCompanyListResponse"
                }
              }
            }
          },
          "400": {
            "description": "INVALID_PAGE_LIMIT when limit is not an integer in 1..200, or INVALID_PAGE_CURSOR when cursor is not an eleven-digit RUC.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks organizations:read, or is a company credential rather than an organization credential. A credential holding companies:manage satisfies organizations:read -- the implication runs that way and never the other.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Organizations"
        ],
        "operationId": "createOrganizationCompany",
        "summary": "Register a client company under the calling organization",
        "description": "Creates the fiscal entity a platform will issue for. Requires an ORGANIZATION credential (apf_org_v2_...) carrying companies:manage; a company credential is refused by the token prefix before any lookup happens. The company is created ACTIVE but can sign nothing: it has no certificate, and since migration 000052 the only route to an ACTIVE certificate is a comprobante SUNAT accepted. Every call writes an ops.audit_events row naming the organization, the credential and the RUC, in the same transaction as the company.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "companies:manage",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[!-~]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOrganizationCompanyRequest"
              },
              "example": {
                "ruc": "20698765432",
                "legalName": "DISTRIBUIDORA SAN MARTIN S.A.C.",
                "environment": "BETA"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Company registered under this organization",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationCompanyResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks companies:manage, or is a company credential rather than an organization credential. A company credential cannot reach these routes at all: it is refused by the token prefix before it is refused by the scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "ORGANIZATION_COMPANY_NOT_FOUND. A company belonging to another organization is reported identically to one that does not exist, so this route cannot be used to discover that a RUC is registered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "COMPANY_RUC_TAKEN when the RUC is already registered, by this organization or any other. IDEMPOTENCY_CONFLICT when the key was reused with a different payload.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/organizations/companies/{id}/certificate": {
      "post": {
        "tags": [
          "Organizations"
        ],
        "operationId": "enrollOrganizationCompanyCertificate",
        "summary": "Upload a client company's signing certificate and SOL credentials",
        "description": "The same body, the same validation, the same sealing boundary and the same DRAFT outcome as POST /api/v2/certificates -- it is that path, reached with the tenant established by auth.bind_organization_company after verifying the company belongs to the caller's organization. The PKCS#12 and the three passwords cross the signing service binding and reach nothing durable in between. Activation is deliberately NOT available here: promoting the DRAFT sends a real signed factura to SUNAT under the client's RUC, and that is performed with the company credential this organization minted.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "companies:manage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[!-~]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EnrollCertificateRequest"
              },
              "example": {
                "environment": "BETA",
                "pfxBase64": "MIIKlgIBAzCCClwGCSqGSIb3DQEHAaCCCk0EggpJMIIKRTCCBgYGCSqGSIb3DQEHAaCCBfcEggXzMIIF7zCCBesGCyqGSIb3DQEMCgECoIIE",
                "pfxPassword": "la-clave-del-pkcs12",
                "solUser": "MODDATOS",
                "solPassword": "moddatos"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay: the version this key already produced",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateEnrollmentReplayResponse"
                }
              }
            }
          },
          "201": {
            "description": "Certificate enrolled as a DRAFT version; nothing signs with it yet",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateEnrolledResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks companies:manage, or is a company credential rather than an organization credential. A company credential cannot reach these routes at all: it is refused by the token prefix before it is refused by the scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "ORGANIZATION_COMPANY_NOT_FOUND. A company belonging to another organization is reported identically to one that does not exist, so this route cannot be used to discover that a RUC is registered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "COMPANY_RUC_TAKEN when the RUC is already registered, by this organization or any other. IDEMPOTENCY_CONFLICT when the key was reused with a different payload.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/organizations/companies/{id}/credentials": {
      "post": {
        "tags": [
          "Organizations"
        ],
        "operationId": "mintCompanyCredential",
        "summary": "Mint an API credential for a client company",
        "description": "Creates a COMPANY credential, which can issue comprobantes. ADR 0016 point 3 names this a privilege escalation and does not pretend otherwise: the mitigation is that it is recorded. The credential row and its ops.audit_events row are written in one transaction, and a DEFERRABLE constraint trigger on auth.api_credentials refuses at COMMIT to admit a credential for an organization-owned company without one -- so an unaudited mint is not a policy violation, it is a failed transaction. The token is returned exactly once.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "companies:manage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[!-~]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MintCompanyCredentialRequest"
              },
              "example": {
                "name": "SaaS - facturacion 20698765432",
                "scopes": [
                  "documents:create",
                  "documents:read",
                  "documents:submit",
                  "documents:pdf",
                  "operations:read",
                  "artifacts:read",
                  "webhooks:manage"
                ],
                "expiresInDays": 90
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Credential minted; the token is in this response and nowhere else",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MintedCompanyCredentialResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks companies:manage, or is a company credential rather than an organization credential. A company credential cannot reach these routes at all: it is refused by the token prefix before it is refused by the scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "ORGANIZATION_COMPANY_NOT_FOUND. A company belonging to another organization is reported identically to one that does not exist, so this route cannot be used to discover that a RUC is registered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "COMPANY_RUC_TAKEN when the RUC is already registered, by this organization or any other. IDEMPOTENCY_CONFLICT when the key was reused with a different payload.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/organizations/usage": {
      "get": {
        "tags": [
          "Organizations"
        ],
        "operationId": "getOrganizationUsage",
        "summary": "Consolidated consumption across every company in the organization",
        "description": "A read, and only a read. ADR 0016 point 6 keeps the quota holder on the company: a platform with fifty clients has fifty subscriptions and each taxpayer pays its own. usage.consume_quota is untouched. Each company is reported against its own plan period, so a mixed portfolio of DAY and MONTH plans is not flattened onto a window this route picked.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "organizations:read",
        "parameters": [
          {
            "name": "metric",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 60,
              "default": "documents.accepted"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Per-company consumption for the requested metric",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationUsageResponse"
                }
              }
            }
          },
          "400": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks companies:manage, or is a company credential rather than an organization credential. A company credential cannot reach these routes at all: it is refused by the token prefix before it is refused by the scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "ORGANIZATION_COMPANY_NOT_FOUND. A company belonging to another organization is reported identically to one that does not exist, so this route cannot be used to discover that a RUC is registered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "COMPANY_RUC_TAKEN when the RUC is already registered, by this organization or any other. IDEMPOTENCY_CONFLICT when the key was reused with a different payload.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "413": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "415": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/organizations/companies/{id}": {
      "get": {
        "tags": [
          "Organizations"
        ],
        "operationId": "getOrganizationCompany",
        "summary": "Read one client company, its capabilities, its certificate and its credentials",
        "description": "Everything needed to answer 'can this client bill, and if not why not'. THE THREE FAILURES ARE ONE ANSWER: a company belonging to another organization, a company that does not exist and an INACTIVE company all return the identical 404, decided by a single SELECT rather than by three branches, so this route cannot be used to discover that a RUC is registered elsewhere. Requires organizations:read; a credential holding companies:manage satisfies it, and one holding only organizations:read can create nothing, mint nothing and enrol nothing.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "organizations:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The company, its capability state, its certificate state and its credentials",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationCompanyDetailResponse"
                }
              }
            }
          },
          "400": {
            "description": "INVALID_PAGE_LIMIT when limit is not an integer in 1..200, or INVALID_PAGE_CURSOR when cursor is not an eleven-digit RUC.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks organizations:read, or is a company credential rather than an organization credential. A credential holding companies:manage satisfies organizations:read -- the implication runs that way and never the other.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "ORGANIZATION_COMPANY_NOT_FOUND. Byte-identical, request id aside, for a company of another organization, a company that does not exist and an INACTIVE one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/consultations/ruc/{ruc}": {
      "get": {
        "tags": [
          "Consultations"
        ],
        "operationId": "consultTaxpayer",
        "summary": "Look a RUC up in the hosted SUNAT padrón — informational, never a validation",
        "description": "Answers what SUNAT's published padrón says about a RUC: the legal name, the literal estado del contribuyente and condición de domicilio, and a coarse classification of each. THIS DOES NOT VALIDATE ANYTHING AND DOES NOT AFFECT ISSUANCE. A RUC this route reports as not registered can still be invoiced, and doing so is neither an error nor a warning: no create, no trigger and no dispatch decision reads this catalogue. The copy is a snapshot and is therefore ALWAYS behind — a company registered since the snapshot was cut is absent from it while being entirely able to receive a comprobante — so treat the answer as information for a human, not as a gate. THERE ARE THREE OUTCOMES AND ONLY TWO OF THEM ARE ABOUT THE TAXPAYER: 200 with registered=true, 200 with registered=false (a snapshot is loaded and this RUC is not in it), and 503 TAXPAYER_REGISTRY_UNAVAILABLE (no snapshot is loaded at all, which is a statement about this service). A client that folds the 503 into registered=false reports an outage of ours as a fact about somebody's customer. Both the literal SUNAT value and the classification are returned: the literal is authoritative and the classification is ours and may be UNKNOWN, so a value SUNAT adds tomorrow is visible rather than silently mapped. registry.publishedOn dates the snapshot the answer came from and is SUNAT's own publication date, never the time this API ingested it. Requires consultations:read, which is disjoint from every documents:* capability: asking who a RUC belongs to must not require a credential that can issue a comprobante.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "consultations:read",
        "parameters": [
          {
            "name": "ruc",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9]{11}$"
            },
            "description": "Exactly eleven digits. Anything else is 422 INVALID_RUC and is never reported as an unregistered taxpayer."
          }
        ],
        "responses": {
          "200": {
            "description": "The padrón's answer. registered=true and registered=false are BOTH this status: an unknown RUC is a successful answer, not an error.",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaxpayerConsultationResponse"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks consultations:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "INVALID_RUC when the path segment is not exactly eleven digits, INVALID_DATE when it is not a real calendar date in YYYY-MM-DD (2026-02-30 is refused rather than rolled over to March), INVALID_CURRENCY when the currency query parameter is not three letters. A malformed input is refused before the catalogue is read, and is never reported as an absent taxpayer or a missing rate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "TAXPAYER_REGISTRY_UNAVAILABLE: no padrón snapshot is loaded, so this API cannot answer the question at all. THIS IS NOT registered=false. It says nothing about the RUC, and it does not affect document issuance — a comprobante may be issued while this route is answering 503.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/consultations/exchange-rate/{date}": {
      "get": {
        "tags": [
          "Consultations"
        ],
        "operationId": "consultExchangeRate",
        "summary": "Resolve the published exchange rate that applies on a given date",
        "description": "Returns the rate to apply to an operation on the requested date, following the rule in D.S. 29-94-EF artículo 5 numeral 17: the rate published on that date, or, on a day with no publication, EL ÚLTIMO PUBLICADO — the last one published on or before it. It never resolves forward and never interpolates. THE DATE IS A REQUIRED PATH SEGMENT WITH NO DEFAULT, on purpose: this service runs on UTC and Peru is UTC-05:00, so a server-side 'today' would resolve to tomorrow's date for every request sent after 19:00 in Lima, and the último-publicado rule would then hand back a rate labelled with a date the caller never asked about. The caller knows its comprobante's issue date; this service does not. BECAUSE THE ANSWER MAY COME FROM ANOTHER DAY, read rateDate, fallback and stalenessDays before displaying anything: fallback=true means rateDate differs from requestedOn, and stalenessDays is how far back the answer was taken from — 2 over a weekend is normal and correct under the rule, while 40 is not a market fact but a loader that stopped running. No ceiling is imposed here, because the norm has none; reporting is this API's half of the job and deciding is the caller's. Both sides are returned: for IGV the answer is sellRate ('promedio ponderado venta', for sales and purchases alike), while buyRate exists because the Impuesto a la Renta rule splits compra/venta by activo/pasivo. Rates are STRINGS with six decimals and must be parsed as decimals — routing a rate that gets multiplied into money through a binary float is the one place a fiscal figure can change value. Informational, like the padrón route: nothing here validates or blocks a document. Requires consultations:read.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "consultations:read",
        "parameters": [
          {
            "name": "date",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            },
            "description": "The date the rate is to APPLY to — normally the comprobante's issue date. A real calendar date in YYYY-MM-DD: 2026-02-30 is refused with 422 INVALID_DATE rather than rolled over to the 1st of March, which would attach a fiscal figure to a date nobody chose."
          },
          {
            "name": "currency",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z]{3}$",
              "default": "USD"
            },
            "description": "Three-letter code, case-insensitive and echoed back upper-cased. Defaults to USD, the only series loaded today. A currency with no loaded series answers 503 rather than falling back to USD."
          }
        ],
        "responses": {
          "200": {
            "description": "The applicable rate. rateDate may differ from requestedOn; when it does, fallback is true and stalenessDays says by how many days.",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExchangeRateConsultationResponse"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks consultations:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "INVALID_RUC when the path segment is not exactly eleven digits, INVALID_DATE when it is not a real calendar date in YYYY-MM-DD (2026-02-30 is refused rather than rolled over to March), INVALID_CURRENCY when the currency query parameter is not three letters. A malformed input is refused before the catalogue is read, and is never reported as an absent taxpayer or a missing rate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "EXCHANGE_RATE_UNAVAILABLE: nothing has ever been published for this currency on or before the requested date — an unloaded currency, an empty series, or a date older than the first row loaded. Distinct from a resolved-but-stale answer, which is a 200 with fallback=true, and it does not affect document issuance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/consultations/dni/{dni}": {
      "get": {
        "tags": [
          "Consultations"
        ],
        "operationId": "consultDni",
        "summary": "Resolve a DNI to a person's NAME — informational, never a validation",
        "description": "Answers what RENIEC's registry holds for an eight-digit DNI: the person's names, and nothing else. Informational like the two routes above — no create, no trigger and no dispatch decision resolves a DNI, so a boleta whose acquirer name was typed by hand is issued exactly as before while this route is answering 503.\n\nONLY THE NAME IS RETURNED, AND THAT IS A DECISION RATHER THAN A GAP. The directory this API calls also publishes a person's date of birth and gender, on a different endpoint. That endpoint is never called. Neither datum has a fiscal purpose in this product — a boleta needs at most the acquirer's name — and under Ley 29733's minimisation principle obtaining data with no purpose is the wrong default, not a feature waiting to be finished. Asking for them would be a change to this published contract, not a field somebody appends to a mapper. AND NOTHING IS STORED: not the name, not the DNI, and not a hash of the DNI. An eight-digit space is exhaustible in seconds, so a digest of a DNI identifies the person exactly as well as the DNI does and is not a de-identification of anything. What is recorded is one metered row per consultation carrying the tenant, the verdict CLASS and the instant, because the platform pays a third party for each call and an unattributable spend is one nobody can bill or cap. If you need the name later, keep it yourself, and keep `consultedAt` beside it — this surface has no cache and will not serve you the same answer twice.\n\nAN ABSENT ANSWER IS NOT AN ABSENT PERSON, and this is the expensive mistake the route is shaped to prevent. There are two answers and seven non-answers. `found: true` and `found: false` are both 200 and are both statements about the person: `false` means the registry was reached, answered, and holds no such record. Everything else is a 502 or a 503 with its own code and is a statement about the network or about this platform — never about the person. 502 DNI_DIRECTORY_AMBIGUOUS is the one to read twice: it means the directory answered 200 while declaring a failure WITHOUT saying which, so this API refuses to guess that the person does not exist. Treating any of the seven as `found: false` writes our outage into somebody's customer record and refuses a real person a real document. Branch on the STATUS CODE first and on `found` second.\n\nRequires consultations:dni, which is NOT consultations:read and is not implied by it. The padrón and exchange-rate routes read free local catalogues about legal entities; this one spends a platform contract, writes a row under row-level security and returns personal data about a natural person. A credential handed to a form so it can prefill a company's legal name must not thereby become a credential that can enumerate people's names.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "consultations:dni",
        "parameters": [
          {
            "name": "dni",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9]{8}$"
            },
            "description": "Exactly eight digits. Anything else is 422 INVALID_DNI, refused before a paid provider request is spent, and is never reported as a person who does not exist."
          }
        ],
        "responses": {
          "200": {
            "description": "The registry's answer. found=true and found=false are BOTH this status: a DNI the registry does not hold is a successful answer, not an error. The keys are the same either way, so branch on found and never on the response shape.",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DniConsultationResponse"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "INSUFFICIENT_SCOPE: the authenticated credential lacks consultations:dni. A credential holding only consultations:read receives this, deliberately. The WWW-Authenticate challenge names the scope to grant, and nothing is spent: no provider request and no metered row.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "INVALID_DNI: the path segment is not exactly eight digits. Refused before the directory is called, so a malformed input never costs a paid request, and it is never reported as found=false. error.details is an ARRAY carrying the rejected candidate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "502": {
            "description": "The directory answered, and what it answered cannot be turned into a verdict about this person. NONE of these is found=false and none carries a Retry-After, because none is fixed by waiting. DNI_DIRECTORY_UNREADABLE: a 200 whose body this API cannot read as an identity. DNI_DIRECTORY_AMBIGUOUS: a 200 declaring a failure without saying which — the code that exists so an unrecognised negative is never guessed into 'this person does not exist'. DNI_DIRECTORY_REFUSED: the directory rejected the request for a reason about the request and not about the DNI, which is a bug on this side. None of the three affects document issuance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "503": {
            "description": "No answer was obtained at all, and again NONE of these is found=false. DNI_DIRECTORY_UNCONFIGURED: this deployment holds no directory key, so nothing was sent. DNI_DIRECTORY_UNAUTHORIZED: the directory refused THIS PLATFORM'S key — it is not about your credential, which is why it is not a 401, and rotating your token will not help. Both need an operator and therefore carry no Retry-After, because one would promise a recovery that never arrives on its own. DNI_DIRECTORY_THROTTLED: the directory is rate-limiting this platform, not you — it is not a 429 because 429 here means your own quota — and it carries Retry-After: 60. DNI_DIRECTORY_UNAVAILABLE: unreachable, timed out or 5xx, with Retry-After: 30. None of the four affects document issuance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "pattern": "^[0-9]+$"
                },
                "description": "Seconds to wait. Present on DNI_DIRECTORY_THROTTLED and DNI_DIRECTORY_UNAVAILABLE only. Absent on DNI_DIRECTORY_UNCONFIGURED and DNI_DIRECTORY_UNAUTHORIZED, because both need an operator and no amount of waiting resolves them."
              }
            }
          }
        }
      }
    },
    "/api/v2/operations/{id}": {
      "get": {
        "tags": [
          "Operations"
        ],
        "operationId": "getOperation",
        "summary": "Get a tenant-owned v2 operation",
        "description": "Resolves any operationId this API has issued, from all five collections: documents, summaries (RC), voided communications (RA), retentions (20) and perceptions (40). resourceKind names the collection the result is read from and resourceId the resource inside it; documentId is present only for comprobantes. An operationId belonging to another tenant is indistinguishable from one that never existed: both answer 404 OPERATION_NOT_FOUND.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "operations:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Operation status",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OperationResponse"
                }
              }
            }
          },
          "401": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated credential lacks operations:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Request blocked by the edge rate limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "rate_limit_exceeded"
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Stable error envelope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Opaque API token",
        "description": "Tenant-bound, scoped and expiring Apifact credential. Migrated legacy credentials are accepted only on deprecated v1 writes and tenant-scoped v2 read, poll and download routes. Each operation names the single scope it requires in x-required-scope; the scope array of the security requirement itself is empty because OpenAPI 3.0 requires it to be for a non-oauth2 scheme."
      }
    },
    "schemas": {
      "CreateOrganizationCompanyRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "ruc",
          "legalName",
          "environment"
        ],
        "properties": {
          "ruc": {
            "type": "string",
            "pattern": "^[0-9]{11}$",
            "description": "The taxpayer's RUC. Ownership is NOT verified here and does not need to be: the company can issue nothing until SUNAT accepts a comprobante signed with a certificate issued to this RUC. Registering a RUC does, however, reserve it platform-wide, and every registration is recorded in the organization's audit log."
          },
          "legalName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "environment": {
            "type": "string",
            "enum": [
              "BETA",
              "PRODUCTION"
            ],
            "description": "The SUNAT environment this company will be enrolled for."
          }
        }
      },
      "OrganizationCompanyResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "resourceId",
          "id",
          "ruc",
          "legalName",
          "status",
          "environment",
          "organizationId",
          "createdAt",
          "certificatePath",
          "credentialsPath"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "resourceId": {
            "type": "string",
            "format": "uuid"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "ruc": {
            "type": "string",
            "pattern": "^[0-9]{11}$"
          },
          "legalName": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "INACTIVE"
            ]
          },
          "environment": {
            "type": "string",
            "enum": [
              "BETA",
              "PRODUCTION"
            ]
          },
          "organizationId": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "certificatePath": {
            "type": "string",
            "description": "Where to upload this company's certificate and SOL credentials. The company can sign nothing until that has happened and the resulting DRAFT has been activated against SUNAT."
          },
          "credentialsPath": {
            "type": "string"
          }
        }
      },
      "MintCompanyCredentialRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "scopes"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "scopes": {
            "type": "array",
            "minItems": 1,
            "maxItems": 16,
            "items": {
              "type": "string",
              "enum": [
                "artifacts:read",
                "certificates:manage",
                "consultations:cpe",
                "consultations:dni",
                "consultations:read",
                "documents:create",
                "documents:pdf",
                "documents:read",
                "documents:submit",
                "documents:validate",
                "operations:read",
                "webhooks:manage"
              ]
            },
            "description": "Scopes for the COMPANY credential being minted. companies:manage is not among them and cannot be: it is refused by a CHECK on auth.api_credential_scopes, because a credential that can both issue comprobantes and mint credentials is the privilege escalation ADR 0016 confines to the organization side."
          },
          "expiresInDays": {
            "type": "integer",
            "minimum": 1,
            "maximum": 365,
            "default": 90
          }
        }
      },
      "MintedCompanyCredentialResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "resourceId",
          "credentialId",
          "token",
          "companyId",
          "companyRuc",
          "scopes",
          "expiresAt",
          "createdAt",
          "auditEventId"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "resourceId": {
            "type": "string",
            "format": "uuid"
          },
          "credentialId": {
            "type": "string",
            "format": "uuid"
          },
          "token": {
            "type": "string",
            "description": "The secret, returned EXACTLY ONCE and never recoverable. Only its SHA-256 is stored. There is no route that re-reads it; a lost token is replaced by minting another and revoking this one."
          },
          "companyId": {
            "type": "string",
            "format": "uuid"
          },
          "companyRuc": {
            "type": "string",
            "pattern": "^[0-9]{11}$"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "auditEventId": {
            "type": "string",
            "format": "uuid",
            "description": "The ops.audit_events row this minting wrote. It is returned because minting a credential is a privilege escalation (ADR 0016 point 3) and the caller is entitled to the reference that records it. The credential and the event commit in one transaction: neither exists without the other."
          }
        }
      },
      "OrganizationUsageEntry": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "companyId",
          "ruc",
          "legalName",
          "status",
          "planCode",
          "metered",
          "periodUnit",
          "periodStart",
          "limit",
          "consumed"
        ],
        "properties": {
          "companyId": {
            "type": "string",
            "format": "uuid"
          },
          "ruc": {
            "type": "string",
            "pattern": "^[0-9]{11}$"
          },
          "legalName": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "INACTIVE"
            ]
          },
          "planCode": {
            "type": "string",
            "nullable": true
          },
          "metered": {
            "type": "boolean",
            "description": "false when nothing caps this company: no active subscription, or no plan limit for the metric."
          },
          "periodUnit": {
            "type": "string",
            "enum": [
              "DAY",
              "MONTH"
            ]
          },
          "periodStart": {
            "type": "string",
            "format": "date"
          },
          "limit": {
            "type": "integer",
            "nullable": true
          },
          "consumed": {
            "type": "integer"
          }
        }
      },
      "OrganizationUsageResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "metric",
          "organizationId",
          "companies"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "metric": {
            "type": "string"
          },
          "organizationId": {
            "type": "string",
            "format": "uuid"
          },
          "companies": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrganizationUsageEntry"
            },
            "description": "One entry per company in the organization, each against its OWN plan period. Billing is unchanged: the quota holder is still the company (ADR 0015), and this is a read."
          }
        }
      },
      "CompanyCapabilityState": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "capability",
          "enabled"
        ],
        "properties": {
          "capability": {
            "type": "string",
            "description": "The PostgreSQL column on core.companies that gates the act, by its own name. THIS IS THE SAME STRING a capability rejection puts in error.details[].requirement, so a 409 and this read use one vocabulary and need no mapping between them. The set is OPEN: it is derived from the company row rather than from a fixed list, so a capability added by a future migration appears here without a contract change. Today it is the nine that exist: boleta_submission_enabled, credit_note_submission_enabled, debit_note_submission_enabled, note_creation_enabled, perception_issuance_enabled, retention_issuance_enabled, submission_publication_enabled, summary_submission_enabled and voided_submission_enabled."
          },
          "enabled": {
            "type": "boolean",
            "description": "EVERY ONE OF THESE DEFAULTS TO false. A newly registered company can create documents and can do very little else until an administrator enables what it needs; that is a deliberate posture and not an oversight, and this field is how you find out which ones are still off before sending a document rather than after."
          }
        }
      },
      "CertificateActivationState": {
        "type": "object",
        "additionalProperties": false,
        "description": "The newest activation probe against one enrolled certificate version. The PROPERTY IS ABSENT when the version was never probed -- OpenAPI 3.0 forbids a $ref with siblings, so a nullable reference is not expressible and an optional property is the idiomatic answer. Activation is the step that promotes a DRAFT to ACTIVE, and since migration 000052 the only route through it is a real signed factura that SUNAT beta accepted.",
        "required": [
          "activationId",
          "status",
          "responseCode",
          "responseMessage",
          "failureCode",
          "startedAt",
          "finishedAt"
        ],
        "properties": {
          "activationId": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "PROBING",
              "ACTIVATED",
              "REJECTED",
              "AMBIGUOUS",
              "FAILED"
            ],
            "description": "REJECTED means SUNAT answered and refused the probe: the certificate or the SOL credentials are not accepted for this RUC, and responseCode carries SUNAT's own code. AMBIGUOUS means the probe was sent and no verdict came back; it is TERMINAL on this version and never retried, because a second probe could be a second comprobante under the client's RUC. Both are states in which the company CANNOT bill, and both need a new enrolment rather than a retry."
          },
          "responseCode": {
            "type": "string",
            "nullable": true,
            "description": "SUNAT's own response code, present once a CDR has been read."
          },
          "responseMessage": {
            "type": "string",
            "nullable": true
          },
          "failureCode": {
            "type": "string",
            "nullable": true,
            "description": "Set instead of responseCode when the attempt settled without a CDR."
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          },
          "finishedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Null exactly while the status is PENDING or PROBING."
          }
        }
      },
      "CertificateVersionState": {
        "type": "object",
        "additionalProperties": false,
        "description": "One enrolled certificate version. Object keys, envelope descriptors and password references are deliberately absent: they are the internal layout of the signing boundary and no caller can act on them. The role serving this route holds column-level SELECT that excludes them outright, so their absence is a privilege rather than a habit.",
        "required": [
          "configurationId",
          "version",
          "status",
          "createdAt",
          "activatedAt",
          "retiredAt",
          "serialNumber",
          "issuer",
          "subjectRuc",
          "notBefore",
          "notAfter",
          "revokedAt"
        ],
        "properties": {
          "configurationId": {
            "type": "string",
            "format": "uuid"
          },
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "status": {
            "type": "string",
            "enum": [
              "DRAFT",
              "PENDING_FIRST_ISSUE",
              "ACTIVE",
              "RETIRED"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "activatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "retiredAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "serialNumber": {
            "type": "string"
          },
          "issuer": {
            "type": "string"
          },
          "subjectRuc": {
            "type": "string",
            "pattern": "^[0-9]{11}$",
            "description": "The RUC the certificate was issued to. It must match the company's own RUC for anything to sign; a mismatch is refused at enrolment."
          },
          "notBefore": {
            "type": "string",
            "format": "date-time"
          },
          "notAfter": {
            "type": "string",
            "format": "date-time",
            "description": "When this certificate stops being usable. Nothing in this API renews it, and an expiry that passes unnoticed stops a client billing on an ordinary Tuesday, so this is the field to put a calendar reminder on."
          },
          "revokedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "activation": {
            "$ref": "#/components/schemas/CertificateActivationState"
          }
        }
      },
      "CompanyCertificateState": {
        "type": "object",
        "additionalProperties": false,
        "description": "TWO VERSIONS ARE REPORTED, NOT ONE, because the two questions differ and either alone answers the other wrongly. 'active' is what signs today; 'latest' is the highest version enrolled, whatever became of it, and it is where a rejected upload shows up. When a company's v3 is ACTIVE and its v4 upload was refused, reporting only 'latest' would say the company cannot bill when it can, and reporting only 'active' would hide the refusal the caller is trying to debug. They are frequently the same version, which is the healthy case. 'active' IS ABSENT, not null, when there is none.",
        "required": [
          "readyToSign",
          "latest"
        ],
        "properties": {
          "readyToSign": {
            "type": "boolean",
            "description": "An ACTIVE version exists, its certificate is unrevoked, and now is inside its validity window. This is the CERTIFICATE half of 'can this client bill' and only that half -- the other half is capabilities, and neither implies the other, because the two are fixed by different people. A company with readyToSign true and submission_publication_enabled false will have its documents accepted and never dispatched."
          },
          "active": {
            "$ref": "#/components/schemas/CertificateVersionState"
          },
          "latest": {
            "$ref": "#/components/schemas/CertificateVersionState"
          }
        }
      },
      "OrganizationCompanyState": {
        "type": "object",
        "additionalProperties": false,
        "description": "The certificate block is ABSENT, not null, for a company that has never enrolled one -- which is every company between its registration and its first certificate upload, and is a different state from having one in some state.",
        "required": [
          "id",
          "ruc",
          "legalName",
          "status",
          "environment",
          "createdAt",
          "capabilities",
          "certificatePath",
          "credentialsPath"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "ruc": {
            "type": "string",
            "pattern": "^[0-9]{11}$"
          },
          "legalName": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "INACTIVE"
            ],
            "description": "Always ACTIVE on this surface. An INACTIVE company is not reported at all -- it is one of the three cases these routes deliberately cannot distinguish, along with a company of another organization and one that does not exist. The enum keeps both values because the underlying column has both and a client should not break the day that changes."
          },
          "environment": {
            "type": "string",
            "enum": [
              "BETA",
              "PRODUCTION"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "capabilities": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompanyCapabilityState"
            },
            "description": "Every capability column of this company, sorted by name. An ARRAY and not an object of fixed keys, so a capability added by a later migration is reported rather than being a contract violation -- and so the entries match error.details[] on a capability rejection key for key."
          },
          "certificate": {
            "$ref": "#/components/schemas/CompanyCertificateState"
          },
          "certificatePath": {
            "type": "string"
          },
          "credentialsPath": {
            "type": "string"
          }
        }
      },
      "CompanyCredentialState": {
        "type": "object",
        "additionalProperties": false,
        "description": "A credential that can act as this company. THE TOKEN IS NOT HERE AND IS NOT BEING WITHHELD: it was returned exactly once by POST .../credentials and only its SHA-256 was ever stored, so no route can re-read it and none ever will. A lost token is replaced by minting another and revoking this one.",
        "required": [
          "credentialId",
          "name",
          "tokenPrefix",
          "tokenFormat",
          "scopes",
          "status",
          "createdAt",
          "expiresAt",
          "revokedAt",
          "lastUsedAt"
        ],
        "properties": {
          "credentialId": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "tokenPrefix": {
            "type": "string",
            "description": "The public selector, apf_v2_<credentialId>_. It carries nothing credentialId does not -- it is derivable from it -- and is returned so that matching a credential in your own configuration against this row needs no string building."
          },
          "tokenFormat": {
            "type": "string",
            "enum": [
              "V2_SHA256",
              "LEGACY_HMAC_SHA256"
            ],
            "description": "LEGACY_HMAC_SHA256 marks a credential migrated from the v1 surface, which authenticates by a different path. They are listed because a credential nobody can see is a credential nobody retires."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What this credential may do. companies:manage and organizations:read can never appear here: both are refused in storage by a CHECK on auth.api_credential_scopes, because a credential that can both issue comprobantes and administer the organization is the escalation ADR 0016 confines to the organization side."
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "EXPIRED",
              "REVOKED"
            ],
            "description": "Derived from revokedAt and expiresAt, in that order of precedence."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "revokedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lastUsedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Recorded at most once a minute, so it is a liveness signal and not an access log. Null means this credential has never authenticated a request."
          }
        }
      },
      "OrganizationCompanyListResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "organizationId",
          "companies",
          "page"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "organizationId": {
            "type": "string",
            "format": "uuid"
          },
          "companies": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrganizationCompanyState"
            }
          },
          "page": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "limit",
              "nextCursor"
            ],
            "properties": {
              "limit": {
                "type": "integer",
                "minimum": 1,
                "maximum": 200
              },
              "nextCursor": {
                "type": "string",
                "pattern": "^[0-9]{11}$",
                "nullable": true,
                "description": "Pass as ?cursor= to get the next page. Non-null exactly when this page came back full, which may also mean there were exactly this many: the last page of a full-sized set costs one further request that returns no companies."
              }
            }
          }
        }
      },
      "OrganizationCompanyDetailResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "resourceId",
          "organizationId",
          "company",
          "credentials"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "resourceId": {
            "type": "string",
            "format": "uuid"
          },
          "organizationId": {
            "type": "string",
            "format": "uuid"
          },
          "company": {
            "$ref": "#/components/schemas/OrganizationCompanyState"
          },
          "credentials": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompanyCredentialState"
            },
            "description": "Every credential of this company, newest first, including any an operator or the console created. Empty is a legitimate answer for a company that has had none minted yet."
          }
        }
      },
      "LegacyInvoiceCreateRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "comprobante",
          "cliente",
          "items"
        ],
        "properties": {
          "comprobante": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "tipoDoc",
              "serie",
              "correlativo",
              "fechaEmision",
              "tipoMoneda"
            ],
            "properties": {
              "tipoOperacion": {
                "type": "string",
                "enum": [
                  "0101"
                ],
                "default": "0101"
              },
              "tipoDoc": {
                "type": "string",
                "enum": [
                  "01",
                  "03"
                ]
              },
              "serie": {
                "type": "string",
                "pattern": "^[FB][A-Z0-9]{3}$"
              },
              "correlativo": {
                "type": "string",
                "pattern": "^\\d{1,8}$"
              },
              "fechaEmision": {
                "type": "string",
                "format": "date"
              },
              "horaEmision": {
                "type": "string",
                "pattern": "^(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d$",
                "default": "00:00:00"
              },
              "tipoMoneda": {
                "type": "string",
                "enum": [
                  "PEN",
                  "USD"
                ]
              },
              "tipoPago": {
                "type": "string",
                "enum": [
                  "Contado"
                ],
                "default": "Contado"
              }
            },
            "oneOf": [
              {
                "properties": {
                  "tipoDoc": {
                    "type": "string",
                    "enum": [
                      "01"
                    ]
                  },
                  "serie": {
                    "type": "string",
                    "pattern": "^F[A-Z0-9]{3}$"
                  }
                }
              },
              {
                "properties": {
                  "tipoDoc": {
                    "type": "string",
                    "enum": [
                      "03"
                    ]
                  },
                  "serie": {
                    "type": "string",
                    "pattern": "^B[A-Z0-9]{3}$"
                  }
                }
              }
            ]
          },
          "cliente": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "tipoDoc",
              "numDoc",
              "rznSocial"
            ],
            "properties": {
              "tipoDoc": {
                "type": "string",
                "enum": [
                  "1",
                  "6"
                ]
              },
              "numDoc": {
                "type": "string",
                "pattern": "^\\d{8,11}$"
              },
              "rznSocial": {
                "type": "string",
                "minLength": 1,
                "maxLength": 500
              },
              "codigoPais": {
                "type": "string",
                "enum": [
                  "PE"
                ],
                "default": "PE"
              },
              "direccion": {
                "type": "string",
                "minLength": 1,
                "maxLength": 500
              }
            },
            "oneOf": [
              {
                "properties": {
                  "tipoDoc": {
                    "type": "string",
                    "enum": [
                      "6"
                    ]
                  },
                  "numDoc": {
                    "type": "string",
                    "pattern": "^(?:10|15|16|17|20)\\d{9}$"
                  }
                }
              },
              {
                "properties": {
                  "tipoDoc": {
                    "type": "string",
                    "enum": [
                      "1"
                    ]
                  },
                  "numDoc": {
                    "type": "string",
                    "pattern": "^\\d{8}$"
                  }
                }
              }
            ]
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "descripcion",
                "unidad",
                "cantidad",
                "mtoValorUnitario",
                "codeAfect"
              ],
              "properties": {
                "codProducto": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 100
                },
                "descripcion": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 1000
                },
                "unidad": {
                  "type": "string",
                  "enum": [
                    "NIU",
                    "KGM",
                    "LTR",
                    "MTR",
                    "HUR",
                    "ZZ"
                  ]
                },
                "cantidad": {
                  "type": "string",
                  "pattern": "^(?:[1-9]\\d*(?:\\.\\d{1,6})?|0\\.(?:[1-9]\\d{0,5}|0[1-9]\\d{0,4}|00[1-9]\\d{0,3}|000[1-9]\\d{0,2}|0000[1-9]\\d?|00000[1-9]))$"
                },
                "mtoValorUnitario": {
                  "type": "string",
                  "pattern": "^(?:[1-9]\\d*(?:\\.\\d{1,6})?|0\\.(?:[1-9]\\d{0,5}|0[1-9]\\d{0,4}|00[1-9]\\d{0,3}|000[1-9]\\d{0,2}|0000[1-9]\\d?|00000[1-9]))$"
                },
                "codeAfect": {
                  "type": "string",
                  "enum": [
                    "10"
                  ]
                },
                "igvPorcent": {
                  "type": "string",
                  "enum": [
                    "18"
                  ],
                  "default": "18"
                }
              }
            }
          }
        },
        "oneOf": [
          {
            "properties": {
              "comprobante": {
                "properties": {
                  "tipoDoc": {
                    "type": "string",
                    "enum": [
                      "01"
                    ]
                  }
                }
              },
              "cliente": {
                "properties": {
                  "tipoDoc": {
                    "type": "string",
                    "enum": [
                      "6"
                    ]
                  },
                  "numDoc": {
                    "type": "string",
                    "pattern": "^(?:10|15|16|17|20)\\d{9}$"
                  }
                }
              }
            }
          },
          {
            "properties": {
              "comprobante": {
                "properties": {
                  "tipoDoc": {
                    "type": "string",
                    "enum": [
                      "03"
                    ]
                  }
                }
              },
              "cliente": {
                "properties": {
                  "tipoDoc": {
                    "type": "string",
                    "enum": [
                      "1"
                    ]
                  },
                  "numDoc": {
                    "type": "string",
                    "pattern": "^\\d{8}$"
                  }
                }
              }
            }
          }
        ]
      },
      "LegacyInvoiceReferenceRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "comprobante"
        ],
        "properties": {
          "comprobante": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "tipoDoc",
              "serie",
              "correlativo"
            ],
            "properties": {
              "tipoDoc": {
                "type": "string",
                "enum": [
                  "01",
                  "03"
                ]
              },
              "serie": {
                "type": "string",
                "pattern": "^[FB][A-Z0-9]{3}$"
              },
              "correlativo": {
                "type": "string",
                "pattern": "^\\d{1,8}$"
              }
            },
            "oneOf": [
              {
                "properties": {
                  "tipoDoc": {
                    "type": "string",
                    "enum": [
                      "01"
                    ]
                  },
                  "serie": {
                    "type": "string",
                    "pattern": "^F[A-Z0-9]{3}$"
                  }
                }
              },
              {
                "properties": {
                  "tipoDoc": {
                    "type": "string",
                    "enum": [
                      "03"
                    ]
                  },
                  "serie": {
                    "type": "string",
                    "pattern": "^B[A-Z0-9]{3}$"
                  }
                }
              }
            ]
          }
        }
      },
      "LegacyInvoiceResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "respuesta"
        ],
        "properties": {
          "respuesta": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "success",
              "status",
              "requestId",
              "operationId",
              "documentId",
              "state",
              "operationStatus",
              "replay",
              "mensaje"
            ],
            "properties": {
              "success": {
                "type": "boolean",
                "enum": [
                  true
                ]
              },
              "status": {
                "type": "integer",
                "enum": [
                  200,
                  201,
                  202
                ]
              },
              "requestId": {
                "type": "string",
                "format": "uuid"
              },
              "operationId": {
                "type": "string",
                "format": "uuid"
              },
              "documentId": {
                "type": "string",
                "format": "uuid"
              },
              "state": {
                "$ref": "#/components/schemas/DocumentState"
              },
              "operationStatus": {
                "type": "string",
                "enum": [
                  "PENDING",
                  "RUNNING",
                  "SUCCEEDED",
                  "FAILED"
                ]
              },
              "replay": {
                "type": "boolean"
              },
              "representations": {
                "type": "array",
                "minItems": 1,
                "maxItems": 1,
                "items": {
                  "$ref": "#/components/schemas/PdfRepresentationMetadata"
                }
              },
              "mensaje": {
                "type": "string"
              }
            }
          }
        }
      },
      "LegacyErrorEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "respuesta"
        ],
        "properties": {
          "respuesta": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "success",
              "status",
              "requestId",
              "code",
              "mensaje"
            ],
            "properties": {
              "success": {
                "type": "boolean",
                "enum": [
                  false
                ]
              },
              "status": {
                "type": "integer",
                "minimum": 400,
                "maximum": 599
              },
              "requestId": {
                "type": "string",
                "format": "uuid"
              },
              "code": {
                "type": "string"
              },
              "mensaje": {
                "type": "string"
              },
              "details": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              }
            }
          }
        }
      },
      "Party": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "documentType",
          "documentNumber",
          "legalName"
        ],
        "properties": {
          "documentType": {
            "type": "string",
            "pattern": "^\\d{1,2}$"
          },
          "documentNumber": {
            "type": "string",
            "pattern": "^[0-9A-Za-z-]{1,15}$"
          },
          "legalName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "tradeName": {
            "type": "string",
            "maxLength": 500
          },
          "countryCode": {
            "type": "string",
            "pattern": "^[A-Z]{2}$",
            "default": "PE"
          },
          "ubigeo": {
            "type": "string",
            "pattern": "^\\d{6}$"
          },
          "address": {
            "type": "string",
            "maxLength": 500
          },
          "district": {
            "type": "string",
            "maxLength": 100
          },
          "province": {
            "type": "string",
            "maxLength": 100
          },
          "department": {
            "type": "string",
            "maxLength": 100
          },
          "establishmentCode": {
            "type": "string",
            "pattern": "^\\d{4}$",
            "default": "0000"
          }
        }
      },
      "Supplier": {
        "type": "object",
        "allOf": [
          {
            "$ref": "#/components/schemas/Party"
          },
          {
            "type": "object",
            "required": [
              "documentType",
              "documentNumber"
            ],
            "properties": {
              "documentType": {
                "type": "string",
                "enum": [
                  "6"
                ]
              },
              "documentNumber": {
                "type": "string",
                "pattern": "^\\d{11}$"
              }
            }
          }
        ]
      },
      "NoteParty": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Party"
          },
          {
            "type": "object",
            "properties": {
              "legalName": {
                "type": "string",
                "minLength": 3,
                "maxLength": 500,
                "pattern": "^\\S(?:[^\\t\\r\\n]*\\S)?$",
                "description": "No leading/trailing or control whitespace. Runtime also enforces XML 1.0 code points."
              },
              "tradeName": {
                "type": "string",
                "minLength": 3,
                "maxLength": 500,
                "pattern": "^\\S(?:[^\\t\\r\\n]*\\S)?$",
                "description": "No leading/trailing or control whitespace. Runtime also enforces XML 1.0 code points."
              },
              "address": {
                "type": "string",
                "maxLength": 200,
                "pattern": "^(?:$|\\S(?:[^\\t\\r\\n]*\\S)?)$",
                "description": "Empty or free of leading/trailing and control whitespace. Runtime also enforces XML 1.0 code points."
              }
            }
          }
        ]
      },
      "NoteSupplier": {
        "allOf": [
          {
            "$ref": "#/components/schemas/NoteParty"
          },
          {
            "type": "object",
            "required": [
              "documentType",
              "documentNumber"
            ],
            "properties": {
              "documentType": {
                "type": "string",
                "enum": [
                  "6"
                ]
              },
              "documentNumber": {
                "type": "string",
                "pattern": "^\\d{11}$"
              },
              "countryCode": {
                "type": "string",
                "enum": [
                  "PE"
                ],
                "default": "PE"
              }
            }
          }
        ]
      },
      "LineAllowanceCharge": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "chargeIndicator",
          "amount"
        ],
        "description": "Catalog 53 line-level allowance (00) or charge (47). Both change the IGV base of the line they belong to and are rejected on a free operation.",
        "properties": {
          "chargeIndicator": {
            "type": "boolean",
            "description": "false is an allowance, true is a charge."
          },
          "reasonCode": {
            "type": "string",
            "enum": [
              "00",
              "47"
            ],
            "description": "Defaults to 00 for an allowance and 47 for a charge; any other pairing is rejected."
          },
          "amount": {
            "$ref": "#/components/schemas/PositiveDecimal"
          }
        }
      },
      "DocumentAllowanceCharge": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "chargeIndicator",
          "amount"
        ],
        "description": "Catalog 53 global allowance (03) or charge (50). Only the variants that do not change the IGV base are accepted; base-changing global adjustments (02/49) are not modelled.",
        "properties": {
          "chargeIndicator": {
            "type": "boolean",
            "description": "false is an allowance, true is a charge."
          },
          "reasonCode": {
            "type": "string",
            "enum": [
              "03",
              "50"
            ],
            "description": "Defaults to 03 for an allowance and 50 for a charge; any other pairing is rejected."
          },
          "amount": {
            "$ref": "#/components/schemas/PositiveDecimal"
          }
        }
      },
      "InvoiceLine": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "description",
          "unitCode",
          "quantity",
          "unitValue",
          "affectationCode"
        ],
        "properties": {
          "productCode": {
            "type": "string",
            "maxLength": 100
          },
          "description": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1000
          },
          "unitCode": {
            "type": "string",
            "minLength": 1,
            "maxLength": 3
          },
          "quantity": {
            "$ref": "#/components/schemas/PositiveDecimal"
          },
          "unitValue": {
            "$ref": "#/components/schemas/PositiveDecimal"
          },
          "affectationCode": {
            "type": "string",
            "enum": [
              "10",
              "11",
              "12",
              "13",
              "14",
              "15",
              "16",
              "20",
              "21",
              "30",
              "31",
              "32",
              "33",
              "34",
              "35",
              "36",
              "40"
            ],
            "description": "Catalog 07. 10/20/30/40 are the onerous profiles; 11-16, 21 and 31-36 are the free-of-charge (gratuita) profiles, which publish a referential value instead of a taxable base. 17 (IVAP) is not supported."
          },
          "igvRate": {
            "type": "string",
            "allOf": [
              {
                "$ref": "#/components/schemas/Decimal"
              }
            ],
            "description": "Defaults to 18 for the taxed catalog 07 codes 10-16 and 0 for every other code; any other value is rejected."
          },
          "icbperUnitAmount": {
            "type": "string",
            "allOf": [
              {
                "$ref": "#/components/schemas/PositiveDecimal"
              }
            ],
            "description": "ICBPER charged per unit (catalog 05 scheme 7152). Absent means no ICBPER; when present the quantity must be a whole number."
          },
          "allowanceCharges": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "$ref": "#/components/schemas/LineAllowanceCharge"
            },
            "description": "Catalog 53 line allowances (00) and charges (47)."
          }
        }
      },
      "NoteLine": {
        "allOf": [
          {
            "$ref": "#/components/schemas/InvoiceLine"
          },
          {
            "type": "object",
            "description": "A note line is a comprobante line with one structure removed and two text fields narrowed. Every catalog 07 affectation, the ICBPER excise and every catalog 03 unit are accepted here exactly as they are on 01/03, because SUNAT's note rules carry all three (NotaCredito2_0 rows 161, 290 and 384; ERROR 2936 outside catalog 03, ERROR 3003 for the exonerada/inafecta/exportacion header totals, ERROR 3306 for the ICBPER total). allowanceCharges is refused: SUNAT computes a note line as quantity x unit value with discounts and charges explicitly removed (ERROR 3271, OBSERVACION 4288 over a boleta), so a note that declared one would be rejected. A discount granted after the fact is expressed as the note's own lines under catalog 09 04, 05 or 08.",
            "not": {
              "anyOf": [
                {
                  "required": [
                    "allowanceCharges"
                  ]
                }
              ]
            },
            "properties": {
              "productCode": {
                "type": "string",
                "maxLength": 30,
                "pattern": "^(?:$|\\S(?:[^\\t\\r\\n]*\\S)?)$",
                "description": "Empty or free of leading/trailing and control whitespace. Runtime also enforces XML 1.0 code points. Narrower than the 100 characters a 01/03 line accepts, because 30 is the length SUNAT states for a note item code (OBSERVACION 4234)."
              },
              "description": {
                "type": "string",
                "minLength": 3,
                "maxLength": 500,
                "pattern": "^\\S(?:[^\\t\\r\\n]*\\S)?$",
                "description": "No leading/trailing or control whitespace. Runtime also enforces XML 1.0 code points."
              }
            }
          }
        ]
      },
      "Decimal": {
        "type": "string",
        "pattern": "^(0|[1-9]\\d*)(\\.\\d{1,6})?$",
        "example": "100.50"
      },
      "PositiveDecimal": {
        "type": "string",
        "pattern": "^(?:[1-9]\\d*(?:\\.\\d{1,6})?|0\\.(?:[1-9]\\d{0,5}|0[1-9]\\d{0,4}|00[1-9]\\d{0,3}|000[1-9]\\d{0,2}|0000[1-9]\\d?|00000[1-9]))$",
        "example": "1"
      },
      "Detraction": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "code",
          "percent",
          "supplierAccount"
        ],
        "description": "SPOT (detraccion). Redirects part of the payment to the supplier's Banco de la Nacion account and never reduces the declared total: PayableAmount, the printed total and the QR IMPORTE_TOTAL stay whole. Accepted on 01 in PEN only, and only together with operationType 1001. The S/ 700 floor is deliberately not enforced here: it varies by annex (transporte de carga uses S/ 400 and several services have no floor) and moves by resolucion, so applicability remains the issuer's responsibility.",
        "properties": {
          "code": {
            "type": "string",
            "pattern": "^\\d{3}$",
            "description": "Catalog 54 code of the good or service subject to the SPOT. The code list is not vendored, so only the shape is validated."
          },
          "percent": {
            "type": "string",
            "pattern": "^(?:100(?:\\.0{1,2})?|\\d{1,2}(?:\\.\\d{1,2})?)$",
            "description": "Rate applied to the document total, greater than 0 and at most 100, e.g. 12 or 1.5. Supplied by the caller rather than derived from code: catalog 54 rates change by resolucion and a table baked into the service would reject legitimate invoices until upgraded. The deposit amount is derived from this rate and never supplied.",
            "example": "12"
          },
          "supplierAccount": {
            "type": "string",
            "pattern": "^\\d{11}$",
            "description": "The supplier's detraction account at the Banco de la Nacion, digits only; normalize the hyphenated form the bank prints.",
            "example": "00065012345"
          },
          "paymentMeansCode": {
            "type": "string",
            "pattern": "^\\d{3}$",
            "default": "001",
            "description": "Catalog 59 means of payment for the deposit; 001 is the deposit in account. The catalog is not vendored, so only the shape is validated."
          }
        }
      },
      "Installment": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "amount",
          "dueDate"
        ],
        "description": "One instalment (cuota) of a credit sale. The identifier is not accepted here: Cuota001, Cuota002 ... are derived from the order of the array, so a schedule can never publish duplicated, missing or out-of-order identifiers.",
        "properties": {
          "amount": {
            "type": "string",
            "pattern": "^(?:[1-9]\\d*\\.\\d{2}|0\\.(?:0[1-9]|[1-9]\\d))$",
            "description": "Amount of this instalment in the document currency, with exactly two decimals and greater than zero. Unlike a unit value this is a settled figure that has to add up to the cent.",
            "example": "400.00"
          },
          "dueDate": {
            "type": "string",
            "format": "date",
            "description": "Maturity of this instalment. It cannot precede the issue date, and the schedule cannot run backwards."
          }
        }
      },
      "InvoiceCustomer": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Party"
          },
          {
            "type": "object",
            "required": [
              "documentType",
              "documentNumber"
            ],
            "properties": {
              "documentType": {
                "type": "string",
                "enum": [
                  "0",
                  "1",
                  "4",
                  "6",
                  "7"
                ],
                "description": "Catalog 06. 6 is a RUC (11 digits) and 1 a DNI (8 digits). On a factura 01 the customer is a RUC, or — only when every line is affectation 40 (export) — a non-domiciled 0 or 7 with a countryCode other than PE and no detraction. A boleta 03 accepts 1, 4 (carne de extranjeria), 7 (passport) and 0 (non-domiciled document without RUC), but never 6. 4 and 7 are refused on a factura because a resident foreigner needs a RUC to use its tax credit; on a boleta, which grants no credit, that reason does not apply."
              },
              "documentNumber": {
                "type": "string",
                "pattern": "^[0-9A-Za-z-]{1,15}$",
                "description": "Shape depends on documentType: 11 digits for 6, 8 digits for 1, and this alphanumeric form for 0, 4 and 7, whose length and alphabet are set by the issuing state. The single character \"-\" is reserved: paired with documentType 0 on a boleta 03 it is SUNAT's marker for a sale that identifies nobody, and it is refused on a factura and with every other code."
              }
            }
          }
        ]
      },
      "AffectedInvoiceReference": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "documentType",
          "series",
          "number"
        ],
        "oneOf": [
          {
            "title": "Factura affected",
            "type": "object",
            "properties": {
              "documentType": {
                "type": "string",
                "enum": [
                  "01"
                ]
              },
              "series": {
                "type": "string",
                "pattern": "^F[A-Z0-9]{3}$"
              }
            }
          },
          {
            "title": "Boleta affected",
            "type": "object",
            "properties": {
              "documentType": {
                "type": "string",
                "enum": [
                  "03"
                ]
              },
              "series": {
                "type": "string",
                "pattern": "^B[A-Z0-9]{3}$"
              }
            }
          }
        ],
        "properties": {
          "documentType": {
            "type": "string",
            "enum": [
              "01",
              "03"
            ],
            "description": "The comprobante the note modifies. It has to agree with the note's own series: SUNAT reads the affected type off that first character and rejects the pair when they disagree (2399 for a B-series credit note that does not modify a boleta, 2400 for a B-series debit note, 2116 for an F-series note that does not modify a factura)."
          },
          "series": {
            "type": "string",
            "pattern": "^[BF][A-Z0-9]{3}$"
          },
          "number": {
            "type": "string",
            "pattern": "^\\d{1,8}$"
          }
        }
      },
      "CreateDocumentRequest": {
        "type": "object",
        "additionalProperties": false,
        "oneOf": [
          {
            "title": "Factura profile",
            "type": "object",
            "properties": {
              "documentType": {
                "type": "string",
                "enum": [
                  "01"
                ]
              },
              "series": {
                "type": "string",
                "pattern": "^F[A-Z0-9]{3}$"
              },
              "customer": {
                "$ref": "#/components/schemas/InvoiceCustomer"
              }
            }
          },
          {
            "title": "Boleta profile",
            "type": "object",
            "not": {
              "anyOf": [
                {
                  "required": [
                    "detraction"
                  ]
                }
              ]
            },
            "properties": {
              "documentType": {
                "type": "string",
                "enum": [
                  "03"
                ]
              },
              "series": {
                "type": "string",
                "pattern": "^B[A-Z0-9]{3}$"
              },
              "customer": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Party"
                  },
                  {
                    "type": "object",
                    "description": "Catalog 06 1 (DNI, 8 digits), 4 (carne de extranjeria), 7 (passport) or 0 (non-domiciled document without RUC); never 6. For the ordinary retail sale that identifies nobody, send documentType 0 with documentNumber \"-\", which is the sentinel SUNAT's own boleta validation reads as \"no identity\". That form is accepted only while the document total stays at or below PEN 700.00 and only in PEN: above it, SUNAT rejects the daily summary that carries the boleta with error 2514.",
                    "properties": {
                      "documentType": {
                        "type": "string",
                        "enum": [
                          "0",
                          "1",
                          "4",
                          "7"
                        ]
                      },
                      "documentNumber": {
                        "type": "string",
                        "pattern": "^[0-9A-Za-z-]{1,15}$"
                      }
                    }
                  }
                ]
              }
            }
          },
          {
            "title": "Credit note profile",
            "type": "object",
            "required": [
              "reasonCode",
              "affectedDocument"
            ],
            "not": {
              "anyOf": [
                {
                  "required": [
                    "operationType"
                  ]
                },
                {
                  "required": [
                    "paymentMethod"
                  ]
                },
                {
                  "required": [
                    "installments"
                  ]
                },
                {
                  "required": [
                    "allowanceCharges"
                  ]
                },
                {
                  "required": [
                    "detraction"
                  ]
                }
              ]
            },
            "properties": {
              "documentType": {
                "type": "string",
                "enum": [
                  "07"
                ]
              },
              "series": {
                "type": "string",
                "pattern": "^[BF][A-Z0-9]{3}$",
                "description": "F when affectedDocument.documentType is 01, B when it is 03. SUNAT reads the affected type off this first character, so the two cannot disagree (errors 2399 and 2116). F000 and B000 are reserved for the platform and are refused with 422 INVALID_DOCUMENT here too: a note is a document like any other and the reservation is not narrowed for it."
              },
              "currency": {
                "type": "string",
                "enum": [
                  "PEN",
                  "USD"
                ],
                "description": "The currency of the comprobante the note modifies, and it must be that same currency (SUNAT 3209). USD is therefore only reachable against a USD comprobante; the equality is checked against the stored payload, not here."
              },
              "reasonCode": {
                "type": "string",
                "enum": [
                  "01",
                  "02",
                  "03",
                  "04",
                  "05",
                  "06",
                  "07",
                  "08",
                  "09"
                ],
                "description": "Catalog 09, and what the code means for the note. 01 annulment, 02 annulment for a wrong RUC, 03 correction of the description and 06 full return void the affected comprobante: the note must reproduce its fiscal lines and totals (only line descriptions may differ), and exactly one such note may ever exist per comprobante. 04 global discount, 05 per-item discount, 07 per-item return, 08 bonus and 09 decrease in value credit part of it: the note carries its own lines and its own amounts, fewer lines if that is what happened, and any number of them may stand against one comprobante. What bounds them is the total: the credit accumulated by every 07 note may not exceed the affected comprobante's payable amount, and the note that would take it past is refused with 409 AFFECTED_FACTURA_CREDIT_EXCEEDED or 409 AFFECTED_BOLETA_CREDIT_EXCEEDED. Over a boleta, 04, 05 and 08 are refused: SUNAT observes them (4367) because a discount or bonification to a final consumer belongs on the boleta itself. The reasons that change the document itself (10, 11, 12, 13) are not accepted."
              },
              "affectedDocument": {
                "$ref": "#/components/schemas/AffectedInvoiceReference"
              },
              "lines": {
                "type": "array",
                "minItems": 1,
                "maxItems": 500,
                "items": {
                  "$ref": "#/components/schemas/NoteLine"
                }
              },
              "supplier": {
                "$ref": "#/components/schemas/NoteSupplier"
              },
              "customer": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/NoteParty"
                  },
                  {
                    "type": "object",
                    "description": "The acquirer of the comprobante the note modifies, and it must be that same acquirer. Over a factura that is a RUC (catalog 06 6, 11 digits) or, when every line is affectation 40 (export), a non-domiciled catalog 06 0 or 7 with a countryCode other than PE. Over a boleta it is whatever the boleta carried: catalog 06 1, 4, 7 or 0 — including 0 with documentNumber \"-\" when the boleta identified nobody, in which case the note must be PEN and its total may not exceed 700.00 (SUNAT 2514). An acquirer identified by a Peruvian document declares PE.",
                    "properties": {
                      "documentType": {
                        "type": "string",
                        "enum": [
                          "6",
                          "0",
                          "1",
                          "4",
                          "7"
                        ]
                      },
                      "documentNumber": {
                        "type": "string",
                        "pattern": "^[0-9A-Za-z-]{1,15}$"
                      }
                    }
                  }
                ]
              }
            }
          },
          {
            "title": "Debit note profile",
            "type": "object",
            "required": [
              "reasonCode",
              "affectedDocument"
            ],
            "not": {
              "anyOf": [
                {
                  "required": [
                    "operationType"
                  ]
                },
                {
                  "required": [
                    "paymentMethod"
                  ]
                },
                {
                  "required": [
                    "installments"
                  ]
                },
                {
                  "required": [
                    "allowanceCharges"
                  ]
                },
                {
                  "required": [
                    "detraction"
                  ]
                }
              ]
            },
            "properties": {
              "documentType": {
                "type": "string",
                "enum": [
                  "08"
                ]
              },
              "series": {
                "type": "string",
                "pattern": "^[BF][A-Z0-9]{3}$",
                "description": "F when affectedDocument.documentType is 01, B when it is 03. SUNAT reads the affected type off this first character, so the two cannot disagree (errors 2400 and 2116). F000 and B000 are reserved for the platform and are refused with 422 INVALID_DOCUMENT here too: a note is a document like any other and the reservation is not narrowed for it."
              },
              "currency": {
                "type": "string",
                "enum": [
                  "PEN",
                  "USD"
                ],
                "description": "The currency of the comprobante the note modifies, and it must be that same currency (SUNAT 3209). USD is therefore only reachable against a USD comprobante; the equality is checked against the stored payload, not here."
              },
              "reasonCode": {
                "type": "string",
                "enum": [
                  "01",
                  "02",
                  "03"
                ],
                "description": "Catalog 10. 01 late-payment interest, 02 increase in value, 03 penalties and other concepts. Catalog 10 has no 04 to 10; 11 and 12 need export or IVAP lines, and 13 does not take effect until 2027-01-01. The affected factura is always referenced, including on 03, which SUNAT lets omit it only until then."
              },
              "affectedDocument": {
                "$ref": "#/components/schemas/AffectedInvoiceReference"
              },
              "lines": {
                "type": "array",
                "minItems": 1,
                "maxItems": 500,
                "items": {
                  "$ref": "#/components/schemas/NoteLine"
                }
              },
              "supplier": {
                "$ref": "#/components/schemas/NoteSupplier"
              },
              "customer": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/NoteParty"
                  },
                  {
                    "type": "object",
                    "description": "The acquirer of the comprobante the note modifies, and it must be that same acquirer. Over a factura that is a RUC (catalog 06 6, 11 digits) or, when every line is affectation 40 (export), a non-domiciled catalog 06 0 or 7 with a countryCode other than PE. Over a boleta it is whatever the boleta carried: catalog 06 1, 4, 7 or 0 — including 0 with documentNumber \"-\" when the boleta identified nobody, in which case the note must be PEN and its total may not exceed 700.00 (SUNAT 2514). An acquirer identified by a Peruvian document declares PE.",
                    "properties": {
                      "documentType": {
                        "type": "string",
                        "enum": [
                          "6",
                          "0",
                          "1",
                          "4",
                          "7"
                        ]
                      },
                      "documentNumber": {
                        "type": "string",
                        "pattern": "^[0-9A-Za-z-]{1,15}$"
                      }
                    }
                  }
                ]
              }
            }
          }
        ],
        "required": [
          "schemaVersion",
          "documentType",
          "series",
          "number",
          "issueDate",
          "currency",
          "supplier",
          "customer",
          "lines"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "1.0"
            ]
          },
          "documentType": {
            "type": "string",
            "enum": [
              "01",
              "03",
              "07",
              "08"
            ],
            "description": "01 requires F-series and a RUC customer, or a non-domiciled catalog 06 0/7 customer when every line is affectation 40 (export); 03 requires a B-series and a customer identified by catalog 06 1, 4, 7 or 0, or catalog 06 0 with documentNumber \"-\" for a PEN sale of at most 700.00 that identifies nobody; 07/08 are PEN and take the series and the customer of the comprobante they modify — F-series and a RUC over a factura, B-series and the boleta's own acquirer (sentinel included) over a boleta."
          },
          "series": {
            "type": "string",
            "pattern": "^[A-Z0-9]{4}$",
            "description": "The per-profile patterns above narrow this: F for a factura, B for a boleta, and either for a note depending on what it affects. Two values are refused on every document type: F000 and B000 are reserved for the platform and answer 422 INVALID_DOCUMENT. F000 is the series the certificate activation probe issues on, and SUNAT records that probe under this tenant's RUC even though it is not a document in this API, so a tenant-issued F000 would collide with a number SUNAT already holds."
          },
          "number": {
            "type": "string",
            "pattern": "^\\d{1,8}$"
          },
          "issueDate": {
            "type": "string",
            "format": "date"
          },
          "issueTime": {
            "type": "string",
            "pattern": "^\\d{2}:\\d{2}:\\d{2}$",
            "default": "00:00:00"
          },
          "currency": {
            "type": "string",
            "enum": [
              "PEN",
              "USD"
            ]
          },
          "operationType": {
            "type": "string",
            "pattern": "^\\d{4}$",
            "default": "0101",
            "description": "Catalog 51. 1001 (operation subject to the SPOT) and the detraction object must be declared together; any other value forbids detraction."
          },
          "paymentMethod": {
            "type": "string",
            "enum": [
              "Contado",
              "Credito"
            ],
            "default": "Contado",
            "description": "Credito requires the installments array and Contado forbids it; the two are the same fact stated twice."
          },
          "installments": {
            "type": "array",
            "minItems": 1,
            "maxItems": 999,
            "items": {
              "$ref": "#/components/schemas/Installment"
            },
            "description": "Schedule of a credit sale, in chronological order, on 01 or 03. The instalments must add up to the cent to what is left pending: the payable amount, or the amount net of the detraction when the operation is subject to the SPOT, because the deposit at the Banco de la Nacion is not owed to the supplier. A schedule that does not settle the pending amount is rejected. The 999 ceiling is what the CuotaNNN identifier can express, not a business quota. The 07/08 note profiles reject the field."
          },
          "reasonCode": {
            "type": "string",
            "enum": [
              "01",
              "02",
              "03",
              "04",
              "05",
              "06",
              "07",
              "08",
              "09"
            ],
            "description": "Reason for a 07 or 08 note, read from catalog 09 or catalog 10 respectively. The two catalogs overlap without agreeing: the value is only meaningful together with documentType, and the note profile above states which codes each type admits. On a 07 the code also decides whether the note voids the affected factura or credits part of it, which is the one place the value changes what the rest of the body has to contain."
          },
          "affectedDocument": {
            "$ref": "#/components/schemas/AffectedInvoiceReference"
          },
          "supplier": {
            "$ref": "#/components/schemas/Supplier"
          },
          "customer": {
            "$ref": "#/components/schemas/InvoiceCustomer"
          },
          "lines": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "items": {
              "$ref": "#/components/schemas/InvoiceLine"
            }
          },
          "allowanceCharges": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "$ref": "#/components/schemas/DocumentAllowanceCharge"
            },
            "description": "Catalog 53 global allowances (03) and charges (50). Accepted on 01/03 only; the 07/08 note profiles reject them."
          },
          "detraction": {
            "$ref": "#/components/schemas/Detraction"
          }
        }
      },
      "SummaryAmount": {
        "type": "string",
        "pattern": "^\\d{1,12}\\.\\d{2}$",
        "description": "A settled summary amount. Exactly two decimals, unlike the up-to-six-decimal unit values of an invoice line.",
        "example": "118.00"
      },
      "SummaryIssuer": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "documentType",
          "documentNumber",
          "legalName"
        ],
        "properties": {
          "documentType": {
            "type": "string",
            "enum": [
              "6"
            ]
          },
          "documentNumber": {
            "type": "string",
            "pattern": "^\\d{11}$"
          },
          "legalName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500,
            "description": "No tab or line-break whitespace and no leading or trailing whitespace. Runtime also enforces XML 1.0 code points."
          },
          "tradeName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          }
        }
      },
      "SummaryCustomer": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "documentType",
          "documentNumber"
        ],
        "description": "Optional on purpose: a boleta below the identification threshold carries no customer, and requiring one here would make those days unsendable.",
        "properties": {
          "documentType": {
            "type": "string",
            "pattern": "^[0-9A-Z]$"
          },
          "documentNumber": {
            "type": "string",
            "pattern": "^[0-9A-Za-z-]{1,15}$"
          }
        }
      },
      "SummaryTaxes": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "igv"
        ],
        "description": "IGV is always declared, even at zero. The optional schemes are omitted from the UBL when absent.",
        "properties": {
          "igv": {
            "$ref": "#/components/schemas/SummaryAmount"
          },
          "isc": {
            "$ref": "#/components/schemas/SummaryAmount"
          },
          "icbper": {
            "$ref": "#/components/schemas/SummaryAmount"
          },
          "other": {
            "$ref": "#/components/schemas/SummaryAmount"
          }
        }
      },
      "SummaryBillingPayments": {
        "type": "object",
        "additionalProperties": false,
        "description": "sac:BillingPayment breakdown: taxable (01), exempt (02), unaffected (03) and free (05). A zero or absent bucket is omitted, because SUNAT reads the presence of a bucket as 'this operation type occurred'.",
        "properties": {
          "taxable": {
            "$ref": "#/components/schemas/SummaryAmount"
          },
          "exempt": {
            "$ref": "#/components/schemas/SummaryAmount"
          },
          "unaffected": {
            "$ref": "#/components/schemas/SummaryAmount"
          },
          "free": {
            "$ref": "#/components/schemas/SummaryAmount"
          }
        }
      },
      "SummaryAffectedDocument": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "documentType",
          "series",
          "number"
        ],
        "description": "The boleta a summarized note affects. Only 03 may be affected inside an RC; a note over a factura belongs to sendBill.",
        "properties": {
          "documentType": {
            "type": "string",
            "enum": [
              "03"
            ]
          },
          "series": {
            "type": "string",
            "pattern": "^B[A-Z0-9]{3}$"
          },
          "number": {
            "type": "string",
            "pattern": "^\\d{1,8}$"
          }
        }
      },
      "SummaryLine": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "documentType",
          "series",
          "number",
          "statusCode",
          "currency",
          "totalAmount",
          "taxes"
        ],
        "properties": {
          "documentType": {
            "type": "string",
            "enum": [
              "03",
              "07",
              "08"
            ]
          },
          "series": {
            "type": "string",
            "pattern": "^B[A-Z0-9]{3}$",
            "description": "Everything reported in an RC is boleta-family, including the notes that affect a boleta, so every series starts with B."
          },
          "number": {
            "type": "string",
            "pattern": "^\\d{1,8}$"
          },
          "statusCode": {
            "type": "string",
            "enum": [
              "1",
              "2",
              "3"
            ],
            "description": "Catalog 19. 1 add, 2 modify, 3 annul. A boleta is annulled with 3 here, never with a void communication."
          },
          "currency": {
            "type": "string",
            "enum": [
              "PEN"
            ]
          },
          "customer": {
            "$ref": "#/components/schemas/SummaryCustomer"
          },
          "totalAmount": {
            "$ref": "#/components/schemas/SummaryAmount"
          },
          "payments": {
            "$ref": "#/components/schemas/SummaryBillingPayments"
          },
          "taxes": {
            "$ref": "#/components/schemas/SummaryTaxes"
          },
          "affectedDocument": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SummaryAffectedDocument"
              }
            ],
            "description": "Required on a 07 or 08 line and rejected on a 03 line: only a summarized note references a document."
          }
        }
      },
      "VoidedLine": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "documentType",
          "series",
          "number",
          "reason"
        ],
        "properties": {
          "documentType": {
            "type": "string",
            "enum": [
              "01",
              "07",
              "08"
            ],
            "description": "03 is deliberately absent. A boleta is annulled by a daily summary line with statusCode 3; an RA over a boleta is rejected by SUNAT while leaving the operator believing it was withdrawn."
          },
          "series": {
            "type": "string",
            "pattern": "^F[A-Z0-9]{3}$"
          },
          "number": {
            "type": "string",
            "pattern": "^\\d{1,8}$"
          },
          "reason": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "cbc:VoidReasonDescription. Free text; SUNAT requires a reason."
          }
        }
      },
      "CreateSummaryRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "documentType",
          "issueDate",
          "referenceDate",
          "correlative",
          "supplier",
          "lines"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "1.0"
            ]
          },
          "documentType": {
            "type": "string",
            "enum": [
              "RC"
            ]
          },
          "issueDate": {
            "type": "string",
            "format": "date",
            "description": "cbc:IssueDate: the day the summary is generated and communicated."
          },
          "referenceDate": {
            "type": "string",
            "format": "date",
            "description": "cbc:ReferenceDate: the day the summarized documents were issued. It cannot be after issueDate."
          },
          "correlative": {
            "type": "string",
            "pattern": "^\\d{1,5}$",
            "description": "N of RUC-RC-YYYYMMDD-N, greater than zero and unique per issuer, environment, type and issue date."
          },
          "supplier": {
            "$ref": "#/components/schemas/SummaryIssuer"
          },
          "lines": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "items": {
              "$ref": "#/components/schemas/SummaryLine"
            }
          }
        }
      },
      "CreateVoidedCommunicationRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "documentType",
          "issueDate",
          "referenceDate",
          "correlative",
          "supplier",
          "lines"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "1.0"
            ]
          },
          "documentType": {
            "type": "string",
            "enum": [
              "RA"
            ]
          },
          "issueDate": {
            "type": "string",
            "format": "date",
            "description": "cbc:IssueDate: the day the void is communicated."
          },
          "referenceDate": {
            "type": "string",
            "format": "date",
            "description": "cbc:ReferenceDate: the issue date of the documents being withdrawn. It cannot be after issueDate."
          },
          "correlative": {
            "type": "string",
            "pattern": "^\\d{1,5}$"
          },
          "supplier": {
            "$ref": "#/components/schemas/SummaryIssuer"
          },
          "lines": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "items": {
              "$ref": "#/components/schemas/VoidedLine"
            }
          }
        }
      },
      "VoidedDeadline": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "status",
          "elapsedDays",
          "limitDays"
        ],
        "description": "Reported for both families and never enforced, but for two different reasons. For a comunicación de baja the window is seven calendar days from the day after the affected comprobante was issued (workbook sheet `Control de Cambios` row 399, which also records that SUNAT delegated the check to the OSE) and SUNAT rejects a late one outright with ERROR 2957; it is still only reported, because a one-day disagreement about the window would destroy a remedy SUNAT would have honoured. For a resumen diario SUNAT merely OBSERVES lateness — OBSERVACIÓN 4443, and an observed document is an accepted document — so a late RC is a valid RC; the limit of seven days is an assumption, since the workbook keys the window as parameter `004: Plazo máximo de envío` and publishes no value for it. EXPIRED on a summary whose lines are all catalog 19 status 1 or 2 is a warning; on one carrying a status 3 it predicts an outright rejection, because annulling late is ERROR 2957 there too.",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "WITHIN_DEADLINE",
              "EXPIRED",
              "INVALID_ORDER"
            ]
          },
          "elapsedDays": {
            "type": "integer"
          },
          "limitDays": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "SummaryOperationResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "operationId",
          "resourceId",
          "summaryType",
          "documentName",
          "state",
          "replay"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "operationId": {
            "type": "string",
            "format": "uuid",
            "description": "Also the durable Workflow instance id, so the handle returned by create is the handle the dispatch runs under."
          },
          "resourceId": {
            "type": "string",
            "format": "uuid"
          },
          "summaryType": {
            "type": "string",
            "enum": [
              "RC",
              "RA"
            ]
          },
          "documentName": {
            "type": "string",
            "pattern": "^[0-9]{11}-R[CA]-[0-9]{8}-[0-9]{1,5}$"
          },
          "state": {
            "$ref": "#/components/schemas/SummaryState"
          },
          "replay": {
            "type": "boolean"
          },
          "deadline": {
            "$ref": "#/components/schemas/VoidedDeadline"
          }
        }
      },
      "SummaryResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "summaryId",
          "operationId",
          "summaryType",
          "environment",
          "issueDate",
          "referenceDate",
          "correlative",
          "documentName",
          "submissionProfileVersion",
          "state",
          "stateVersion",
          "lineCount",
          "ticket",
          "lastStatusCode",
          "responseCode",
          "responseMessage",
          "errorCode",
          "references",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "summaryId": {
            "type": "string",
            "format": "uuid"
          },
          "operationId": {
            "type": "string",
            "format": "uuid"
          },
          "summaryType": {
            "type": "string",
            "enum": [
              "RC",
              "RA"
            ]
          },
          "environment": {
            "type": "string",
            "enum": [
              "BETA",
              "PRODUCTION"
            ]
          },
          "issueDate": {
            "type": "string",
            "format": "date"
          },
          "referenceDate": {
            "type": "string",
            "format": "date"
          },
          "correlative": {
            "type": "string",
            "pattern": "^\\d{1,5}$"
          },
          "documentName": {
            "type": "string",
            "pattern": "^[0-9]{11}-R[CA]-[0-9]{8}-[0-9]{1,5}$"
          },
          "submissionProfileVersion": {
            "type": "string",
            "enum": [
              "sunat-summary-rc-v1",
              "sunat-voided-ra-v1"
            ]
          },
          "state": {
            "$ref": "#/components/schemas/SummaryState"
          },
          "stateVersion": {
            "type": "integer",
            "minimum": 1
          },
          "lineCount": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500
          },
          "ticket": {
            "type": "string",
            "nullable": true,
            "description": "The SUNAT ticket. It is the only handle getStatus accepts, so it is published for out-of-band reconciliation of an AMBIGUOUS dispatch."
          },
          "lastStatusCode": {
            "type": "string",
            "nullable": true
          },
          "responseCode": {
            "type": "string",
            "nullable": true
          },
          "responseMessage": {
            "type": "string",
            "nullable": true
          },
          "errorCode": {
            "type": "string",
            "nullable": true
          },
          "references": {
            "$ref": "#/components/schemas/SummaryReferenceCounts"
          },
          "deadline": {
            "$ref": "#/components/schemas/VoidedDeadline"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SummaryReferenceCounts": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "total",
          "linked",
          "unlinked"
        ],
        "description": "How many of this communication's lines name a comprobante this API holds. `unlinked` is not an error count: a line naming a comprobante issued through another provider, on paper under a contingency authorisation, or before this tenant integrated, is valid and is accepted. What it means is that no local guarantee covers it — an unlinked line takes no claim, so a second baja over the same comprobante cannot be detected here and SUNAT's ERROR 2323 is the only thing behind it.",
        "properties": {
          "total": {
            "type": "integer",
            "minimum": 0,
            "maximum": 500
          },
          "linked": {
            "type": "integer",
            "minimum": 0,
            "maximum": 500
          },
          "unlinked": {
            "type": "integer",
            "minimum": 0,
            "maximum": 500
          }
        }
      },
      "SummaryState": {
        "type": "string",
        "description": "The RC/RA state machine. It is deliberately not DocumentState: a summary has no RECEIVED or CANCELLED, and TICKET_PENDING is the state it spends most of its life in.",
        "enum": [
          "VALIDATED",
          "XML_BUILT",
          "SIGNED",
          "SUBMISSION_PENDING",
          "SUBMITTED",
          "TICKET_PENDING",
          "ACCEPTED",
          "OBSERVED",
          "REJECTED",
          "FAILED_RETRYABLE",
          "FAILED_FINAL",
          "AMBIGUOUS"
        ]
      },
      "PaymentRegimeParty": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "documentType",
          "documentNumber",
          "legalName"
        ],
        "description": "cac:AgentParty and cac:ReceiverParty. Both are always RUC holders (schemeID 6). The agent is the issuer of the document; the receiver is the supplier being withheld from (CRE) or the customer being charged (CPE). Deliberately not named supplier/customer: in a retention the receiver is the supplier, and forcing the invoice vocabulary here would produce a supplier that is sometimes the issuer and sometimes not.",
        "properties": {
          "documentType": {
            "type": "string",
            "enum": [
              "6"
            ]
          },
          "documentNumber": {
            "type": "string",
            "pattern": "^\\d{11}$"
          },
          "legalName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "tradeName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "countryCode": {
            "type": "string",
            "enum": [
              "PE"
            ],
            "default": "PE"
          },
          "ubigeo": {
            "type": "string",
            "pattern": "^\\d{6}$"
          },
          "address": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "district": {
            "type": "string",
            "minLength": 1,
            "maxLength": 30
          },
          "province": {
            "type": "string",
            "minLength": 1,
            "maxLength": 30,
            "description": "ASSUMPTION: cbc:CityName, per SUNAT's own XSL comments. A widely used PHP implementation maps province and department the other way round; both are optional and SUNAT flags neither, so the divergence is invisible to validation."
          },
          "department": {
            "type": "string",
            "minLength": 1,
            "maxLength": 30
          },
          "establishmentCode": {
            "type": "string",
            "pattern": "^\\d{4}$",
            "default": "0000",
            "description": "Inherited from the shared party schema. Neither the Retention nor the Perception root has a slot for it, so it is accepted and never emitted."
          }
        }
      },
      "PaymentRegimePayment": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "amount",
          "date"
        ],
        "description": "cac:Payment of a referenced document: the instalment being settled. The XSD allows at most ONE payment per reference, so a document paid in instalments is declared as several references sharing the same series/number and differing in this id.",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^\\d{1,9}$"
          },
          "amount": {
            "type": "string",
            "pattern": "^(?:0|[1-9]\\d{0,11})(?:\\.\\d{1,2})?$",
            "description": "Amount paid or collected, in the currency of the referenced document. ASSUMPTION: this, and not the referenced total, is the base the regime rate applies to."
          },
          "date": {
            "type": "string",
            "format": "date"
          }
        }
      },
      "PaymentRegimeExchangeRate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "sourceCurrency",
          "rate",
          "date"
        ],
        "description": "Required exactly when the referenced document is not PEN, and rejected when it is: every regime amount is PEN, so a foreign-currency payment needs the rate that takes it there.",
        "properties": {
          "sourceCurrency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$"
          },
          "targetCurrency": {
            "type": "string",
            "enum": [
              "PEN"
            ],
            "default": "PEN"
          },
          "rate": {
            "type": "string",
            "pattern": "^(?:0|[1-9]\\d{0,3})(?:\\.\\d{1,6})?$"
          },
          "date": {
            "type": "string",
            "format": "date"
          }
        }
      },
      "RetentionDocumentReference": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "documentType",
          "series",
          "number",
          "issueDate",
          "totalAmount",
          "payment",
          "regimeDate"
        ],
        "properties": {
          "documentType": {
            "type": "string",
            "enum": [
              "01",
              "08"
            ],
            "description": "SUNAT's XSL also accepts 12, 07 and 20. They are outside this profile: a credit note makes the payment and retention blocks optional, which is a different arithmetic shape with no official example to pin it."
          },
          "series": {
            "type": "string",
            "pattern": "^F[A-Z0-9]{3}$"
          },
          "number": {
            "type": "string",
            "pattern": "^\\d{1,8}$"
          },
          "issueDate": {
            "type": "string",
            "format": "date"
          },
          "totalAmount": {
            "type": "string",
            "pattern": "^(?:0|[1-9]\\d{0,11})(?:\\.\\d{1,2})?$"
          },
          "currency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$",
            "default": "PEN"
          },
          "payment": {
            "$ref": "#/components/schemas/PaymentRegimePayment"
          },
          "regimeDate": {
            "type": "string",
            "format": "date"
          },
          "exchangeRate": {
            "$ref": "#/components/schemas/PaymentRegimeExchangeRate"
          }
        }
      },
      "PerceptionDocumentReference": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "documentType",
          "series",
          "number",
          "issueDate",
          "totalAmount",
          "payment",
          "regimeDate"
        ],
        "properties": {
          "documentType": {
            "type": "string",
            "enum": [
              "01",
              "03",
              "08"
            ],
            "description": "A perception may reference a boleta, because it reaches a customer who need not hold a RUC on the referenced sale. A retention may not."
          },
          "series": {
            "type": "string",
            "pattern": "^[FB][A-Z0-9]{3}$"
          },
          "number": {
            "type": "string",
            "pattern": "^\\d{1,8}$"
          },
          "issueDate": {
            "type": "string",
            "format": "date"
          },
          "totalAmount": {
            "type": "string",
            "pattern": "^(?:0|[1-9]\\d{0,11})(?:\\.\\d{1,2})?$"
          },
          "currency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$",
            "default": "PEN"
          },
          "payment": {
            "$ref": "#/components/schemas/PaymentRegimePayment"
          },
          "regimeDate": {
            "type": "string",
            "format": "date"
          },
          "exchangeRate": {
            "$ref": "#/components/schemas/PaymentRegimeExchangeRate"
          }
        }
      },
      "CreateRetentionRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "documentType",
          "series",
          "number",
          "issueDate",
          "currency",
          "regimeCode",
          "regimePercent",
          "agent",
          "receiver",
          "documents"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "1.0"
            ]
          },
          "documentType": {
            "type": "string",
            "enum": [
              "20"
            ]
          },
          "series": {
            "type": "string",
            "pattern": "^R[A-Z0-9]{3}$"
          },
          "number": {
            "type": "string",
            "pattern": "^\\d{1,8}$"
          },
          "issueDate": {
            "type": "string",
            "format": "date"
          },
          "issueTime": {
            "type": "string",
            "default": "00:00:00"
          },
          "currency": {
            "type": "string",
            "enum": [
              "PEN"
            ],
            "description": "Both regimes settle in PEN only; SUNAT rejects any other currency on the header totals."
          },
          "regimeCode": {
            "type": "string",
            "pattern": "^\\d{2}$",
            "description": "Catalog 23. The code/rate pair is NOT cross-checked locally: SUNAT checks it server-side (error 2619) and a rate table baked into this build would reject legitimate documents the day a resolución moves one."
          },
          "regimePercent": {
            "type": "string",
            "pattern": "^(?:100(?:\\.0{1,2})?|\\d{1,2}(?:\\.\\d{1,2})?)$"
          },
          "note": {
            "type": "string",
            "minLength": 1,
            "maxLength": 250
          },
          "agent": {
            "$ref": "#/components/schemas/PaymentRegimeParty"
          },
          "receiver": {
            "$ref": "#/components/schemas/PaymentRegimeParty"
          },
          "documents": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "items": {
              "$ref": "#/components/schemas/RetentionDocumentReference"
            }
          }
        }
      },
      "CreatePerceptionRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "documentType",
          "series",
          "number",
          "issueDate",
          "currency",
          "regimeCode",
          "regimePercent",
          "agent",
          "receiver",
          "documents"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "1.0"
            ]
          },
          "documentType": {
            "type": "string",
            "enum": [
              "40"
            ]
          },
          "series": {
            "type": "string",
            "pattern": "^P[A-Z0-9]{3}$"
          },
          "number": {
            "type": "string",
            "pattern": "^\\d{1,8}$"
          },
          "issueDate": {
            "type": "string",
            "format": "date"
          },
          "issueTime": {
            "type": "string",
            "default": "00:00:00"
          },
          "currency": {
            "type": "string",
            "enum": [
              "PEN"
            ]
          },
          "regimeCode": {
            "type": "string",
            "pattern": "^\\d{2}$",
            "description": "Catalog 22, cross-checked by SUNAT against the rate (error 2603)."
          },
          "regimePercent": {
            "type": "string",
            "pattern": "^(?:100(?:\\.0{1,2})?|\\d{1,2}(?:\\.\\d{1,2})?)$"
          },
          "note": {
            "type": "string",
            "minLength": 1,
            "maxLength": 250
          },
          "agent": {
            "$ref": "#/components/schemas/PaymentRegimeParty"
          },
          "receiver": {
            "$ref": "#/components/schemas/PaymentRegimeParty"
          },
          "documents": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "items": {
              "$ref": "#/components/schemas/PerceptionDocumentReference"
            }
          }
        }
      },
      "PaymentRegimeOperationResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "operationId",
          "resourceId",
          "documentType",
          "documentName",
          "state",
          "replay"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "operationId": {
            "type": "string",
            "format": "uuid",
            "description": "Allocated at creation and reserved as the durable dispatch handle, so the handle returned here is the handle a future dispatch will run under."
          },
          "resourceId": {
            "type": "string",
            "format": "uuid"
          },
          "documentType": {
            "type": "string",
            "enum": [
              "20",
              "40"
            ]
          },
          "documentName": {
            "type": "string",
            "pattern": "^[0-9]{11}-(20|40)-[RP][A-Z0-9]{3}-[0-9]{1,8}$",
            "description": "The SUNAT submission name. It is also the ZIP entry base name and the CDR entry base name; SUNAT rejects a name that disagrees with the document it carries with error 1049."
          },
          "state": {
            "$ref": "#/components/schemas/PaymentRegimeState"
          },
          "replay": {
            "type": "boolean"
          }
        }
      },
      "PaymentRegimeReference": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "referenceIndex",
          "referenceDocumentType",
          "referenceSeries",
          "referenceNumber",
          "referenceIssueDate",
          "referenceCurrency",
          "referenceTotalAmount",
          "paymentId",
          "paidAmount",
          "paymentDate",
          "regimeDate",
          "exchangeRate",
          "exchangeDate",
          "baseAmount",
          "regimeAmount",
          "netAmount"
        ],
        "description": "One sac:SUNAT{Retention,Perception}DocumentReference as stored. baseAmount, regimeAmount and netAmount are always PEN and always derived; paidAmount stays in the referenced document currency.",
        "properties": {
          "referenceIndex": {
            "type": "integer",
            "minimum": 1
          },
          "referenceDocumentType": {
            "type": "string",
            "enum": [
              "01",
              "03",
              "08"
            ]
          },
          "referenceSeries": {
            "type": "string",
            "pattern": "^[FB][A-Z0-9]{3}$"
          },
          "referenceNumber": {
            "type": "string",
            "pattern": "^\\d{1,8}$"
          },
          "referenceIssueDate": {
            "type": "string",
            "format": "date"
          },
          "referenceCurrency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$"
          },
          "referenceTotalAmount": {
            "type": "string"
          },
          "paymentId": {
            "type": "string",
            "pattern": "^\\d{1,9}$"
          },
          "paidAmount": {
            "type": "string"
          },
          "paymentDate": {
            "type": "string",
            "format": "date"
          },
          "regimeDate": {
            "type": "string",
            "format": "date"
          },
          "exchangeRate": {
            "type": "string",
            "nullable": true
          },
          "exchangeDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "baseAmount": {
            "type": "string",
            "description": "The payment converted to PEN and rounded to the cent BEFORE the rate is applied. ASSUMPTION, recorded as a database constraint so a contradiction fails loudly."
          },
          "regimeAmount": {
            "type": "string"
          },
          "netAmount": {
            "type": "string"
          }
        }
      },
      "PaymentRegimeResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "paymentRegimeId",
          "operationId",
          "documentType",
          "environment",
          "series",
          "number",
          "issueDate",
          "documentName",
          "regimeCode",
          "regimePercent",
          "agentRuc",
          "receiverRuc",
          "totalRegimeAmount",
          "totalNetAmount",
          "submissionProfileVersion",
          "state",
          "stateVersion",
          "responseCode",
          "responseMessage",
          "errorCode",
          "references",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "paymentRegimeId": {
            "type": "string",
            "format": "uuid"
          },
          "operationId": {
            "type": "string",
            "format": "uuid"
          },
          "documentType": {
            "type": "string",
            "enum": [
              "20",
              "40"
            ]
          },
          "environment": {
            "type": "string",
            "enum": [
              "BETA",
              "PRODUCTION"
            ]
          },
          "series": {
            "type": "string",
            "pattern": "^[RP][A-Z0-9]{3}$"
          },
          "number": {
            "type": "string",
            "pattern": "^\\d{1,8}$"
          },
          "issueDate": {
            "type": "string",
            "format": "date"
          },
          "documentName": {
            "type": "string",
            "pattern": "^[0-9]{11}-(20|40)-[RP][A-Z0-9]{3}-[0-9]{1,8}$"
          },
          "regimeCode": {
            "type": "string",
            "pattern": "^\\d{2}$"
          },
          "regimePercent": {
            "type": "string"
          },
          "agentRuc": {
            "type": "string",
            "pattern": "^\\d{11}$"
          },
          "receiverRuc": {
            "type": "string",
            "pattern": "^\\d{11}$"
          },
          "totalRegimeAmount": {
            "type": "string",
            "description": "cbc:TotalInvoiceAmount of the header: the sum of the regime amounts."
          },
          "totalNetAmount": {
            "type": "string",
            "description": "sac:SUNATTotalPaid / sac:SUNATTotalCashed. ASSUMPTION: the sum of the per-reference NET amounts, not the gross of the payments."
          },
          "submissionProfileVersion": {
            "type": "string",
            "enum": [
              "sunat-retention-cre-v1",
              "sunat-perception-cpe-v1"
            ]
          },
          "state": {
            "$ref": "#/components/schemas/PaymentRegimeState"
          },
          "stateVersion": {
            "type": "integer",
            "minimum": 1
          },
          "responseCode": {
            "type": "string",
            "nullable": true,
            "description": "SUNAT's response code, read from the CDR. Never present without the CDR digest that backs it: a fiscal verdict without a CDR is refused by a CHECK constraint, not merely avoided in code."
          },
          "responseMessage": {
            "type": "string",
            "nullable": true
          },
          "errorCode": {
            "type": "string",
            "nullable": true,
            "description": "Set instead of responseCode when the attempt settled without a CDR, which is always an AMBIGUOUS outcome once the dispatch fence exists."
          },
          "references": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/PaymentRegimeReference"
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PaymentRegimeState": {
        "type": "string",
        "description": "The CRE/CPE state machine. Deliberately not DocumentState: a CRE has no RECEIVED or CANCELLED. TICKET_PENDING is reachable only if SUNAT's 'otros CPE' service answers with a ticket instead of a CDR — ADR 0012 assumption 9 says it does not, and that assumption is not corroborated, so the state is published rather than hidden. AMBIGUOUS means an envelope may have reached SUNAT and no CDR came back; it is never redispatched automatically.",
        "enum": [
          "VALIDATED",
          "XML_BUILT",
          "SIGNED",
          "SUBMISSION_PENDING",
          "SUBMITTED",
          "TICKET_PENDING",
          "ACCEPTED",
          "OBSERVED",
          "REJECTED",
          "FAILED_RETRYABLE",
          "FAILED_FINAL",
          "AMBIGUOUS"
        ]
      },
      "DocumentOperationResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "operationId",
          "resourceId",
          "state",
          "replay"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "operationId": {
            "type": "string",
            "format": "uuid"
          },
          "resourceId": {
            "type": "string",
            "format": "uuid"
          },
          "state": {
            "$ref": "#/components/schemas/DocumentState"
          },
          "replay": {
            "type": "boolean"
          }
        }
      },
      "ValidationFinding": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "code",
          "status",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "The error.code POST /api/v2/documents would answer with, verbatim: INVALID_DOCUMENT, DOCUMENT_ARITHMETIC_INVALID, SUPPLIER_RUC_MISMATCH, NOTE_CREATION_DISABLED, IDEMPOTENCY_CONFLICT, AFFECTED_DOCUMENT_NOT_FOUND, AFFECTED_DOCUMENT_NOT_ELIGIBLE, AFFECTED_DOCUMENT_MISMATCH, AFFECTED_DOCUMENT_ALREADY_CANCELLED, AFFECTED_FACTURA_CREDIT_EXCEEDED or AFFECTED_BOLETA_CREDIT_EXCEEDED."
          },
          "status": {
            "type": "integer",
            "description": "The HTTP status POST /api/v2/documents would answer with."
          },
          "message": {
            "type": "string"
          },
          "details": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "DocumentTotals": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "currency",
          "taxableAmount",
          "exemptAmount",
          "unaffectedAmount",
          "exportAmount",
          "freeAmount",
          "freeIgvAmount",
          "lineExtensionAmount",
          "igvAmount",
          "icbperAmount",
          "taxAmount",
          "taxInclusiveAmount",
          "allowanceTotalAmount",
          "chargeTotalAmount",
          "payableAmount"
        ],
        "properties": {
          "currency": {
            "type": "string"
          },
          "taxableAmount": {
            "type": "string"
          },
          "exemptAmount": {
            "type": "string"
          },
          "unaffectedAmount": {
            "type": "string"
          },
          "exportAmount": {
            "type": "string"
          },
          "freeAmount": {
            "type": "string",
            "description": "Referential value of the free operations (scheme 9996)."
          },
          "freeIgvAmount": {
            "type": "string",
            "description": "IGV of the free operations. Reported, never payable."
          },
          "lineExtensionAmount": {
            "type": "string"
          },
          "igvAmount": {
            "type": "string",
            "description": "The QR IGV_TOTAL."
          },
          "icbperAmount": {
            "type": "string"
          },
          "taxAmount": {
            "type": "string",
            "description": "igvAmount + icbperAmount."
          },
          "taxInclusiveAmount": {
            "type": "string"
          },
          "allowanceTotalAmount": {
            "type": "string"
          },
          "chargeTotalAmount": {
            "type": "string"
          },
          "payableAmount": {
            "type": "string",
            "description": "cbc:PayableAmount, the printed total and the QR IMPORTE_TOTAL."
          },
          "detraction": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "code",
              "percent",
              "baseAmount",
              "amount",
              "netPayableAmount"
            ],
            "description": "Present only when the operation is subject to the SPOT. The split never reduces payableAmount: amount is what the acquirer deposits at the Banco de la Nacion and netPayableAmount is what reaches the supplier.",
            "properties": {
              "code": {
                "type": "string"
              },
              "percent": {
                "type": "string"
              },
              "baseAmount": {
                "type": "string"
              },
              "amount": {
                "type": "string"
              },
              "netPayableAmount": {
                "type": "string"
              }
            }
          },
          "credit": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "pendingAmount",
              "installments"
            ],
            "description": "Present only on a Credito sale. pendingAmount is what is financed — payableAmount, or the detraction's netPayableAmount under the SPOT — and the instalments settle exactly it.",
            "properties": {
              "pendingAmount": {
                "type": "string"
              },
              "installments": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "amount",
                    "dueDate"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Cuota001, Cuota002, ... Derived from the schedule order and never supplied; it reaches the XML, so it is published here."
                    },
                    "amount": {
                      "type": "string"
                    },
                    "dueDate": {
                      "type": "string",
                      "format": "date"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "DocumentValidationResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "valid",
          "findings"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "valid": {
            "type": "boolean",
            "description": "True exactly when findings is empty, which is exactly when POST /api/v2/documents would answer 200 or 201 for this body and this credential."
          },
          "findings": {
            "type": "array",
            "description": "Empty when valid. Otherwise in the order the create route evaluates them: findings[0] is the one it would answer with, and the rest are the problems it would only have reported on the attempt after that.",
            "items": {
              "$ref": "#/components/schemas/ValidationFinding"
            }
          },
          "totals": {
            "$ref": "#/components/schemas/DocumentTotals"
          }
        }
      },
      "DocumentResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "documentId",
          "environment",
          "documentType",
          "series",
          "number",
          "state",
          "fiscalState",
          "stateVersion",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "documentId": {
            "type": "string",
            "format": "uuid"
          },
          "environment": {
            "type": "string",
            "enum": [
              "BETA",
              "PRODUCTION"
            ]
          },
          "documentType": {
            "type": "string",
            "enum": [
              "01",
              "03",
              "07",
              "08"
            ]
          },
          "series": {
            "type": "string"
          },
          "number": {
            "type": "string"
          },
          "state": {
            "$ref": "#/components/schemas/DocumentState"
          },
          "fiscalState": {
            "$ref": "#/components/schemas/DocumentFiscalState"
          },
          "stateVersion": {
            "type": "integer",
            "minimum": 1
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "responseCode": {
            "type": "string",
            "nullable": true,
            "description": "SUNAT CDR `cbc:ResponseCode`. `0` is an acceptance; anything else is the rejection code. Null until SUNAT has answered."
          },
          "responseMessage": {
            "type": "string",
            "nullable": true,
            "description": "SUNAT CDR `cbc:Description`, verbatim."
          },
          "sunat": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SunatResponseGuidance"
              }
            ],
            "nullable": true,
            "description": "Classification of `responseCode` by the versioned SUNAT response catalog. Null until SUNAT has answered. It classifies the CDR `cbc:ResponseCode` and nothing else, which is why an observed comprobante reports severity ACCEPTED here rather than OBSERVATION: SUNAT answers an observed document with ResponseCode `0` and carries the observations as `cbc:Note` entries, and `state` is the field that reports OBSERVED. Read `state` for whether there were observations and this block for what to do about the code."
          }
        }
      },
      "SunatResponseGuidance": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "severity",
          "standing",
          "action",
          "guidance",
          "catalogVersion"
        ],
        "properties": {
          "severity": {
            "type": "string",
            "enum": [
              "ACCEPTED",
              "OBSERVATION",
              "REJECTION",
              "EXCEPTION",
              "UNKNOWN"
            ],
            "description": "Which class SUNAT's programmer manual puts the code in. OBSERVATION means the comprobante was ACCEPTED and registered, with warnings."
          },
          "standing": {
            "type": "string",
            "enum": [
              "REGISTERED",
              "NOT_REGISTERED",
              "MAY_BE_REGISTERED"
            ],
            "description": "Whether the comprobante exists fiscally at SUNAT. MAY_BE_REGISTERED means it must never be sent again without reconciling first."
          },
          "action": {
            "type": "string",
            "enum": [
              "NONE",
              "REVIEW_OBSERVATION",
              "FIX_AND_REISSUE",
              "FIX_AND_RESUBMIT",
              "RETRY_LATER",
              "FIX_CREDENTIALS",
              "RECONCILE_DO_NOT_RESEND"
            ],
            "description": "What the integrator should do next."
          },
          "guidance": {
            "type": "string",
            "description": "One sentence of human-readable guidance, derived from `action`."
          },
          "catalogVersion": {
            "type": "string",
            "nullable": true,
            "description": "Version of the SUNAT response catalog that produced this classification, recorded per attempt so an old verdict stays auditable. Always the version stored on the attempt, never the one the running service holds: reporting today's catalog for yesterday's verdict would forge the audit trail. Null, and present, when the attempt was finalized before migration 000048 added the column — the version was not recorded, and inventing one is the fabrication that migration refuses."
          }
        }
      },
      "OperationResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "operationId",
          "resourceKind",
          "resourceId",
          "operationType",
          "status",
          "startedAt",
          "finishedAt"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "operationId": {
            "type": "string",
            "format": "uuid"
          },
          "documentId": {
            "type": "string",
            "format": "uuid",
            "description": "The comprobante this operation acts on. Present if and only if resourceKind is document, where it repeats resourceId; absent for every other kind, because a summary, void communication, retention or perception is not a row in the document collection."
          },
          "resourceKind": {
            "type": "string",
            "enum": [
              "document",
              "summary",
              "voided",
              "retention",
              "perception"
            ],
            "description": "Which collection owns resourceId, and therefore where the result is read: document -> /api/v2/documents/{id}, summary -> /api/v2/summaries/{id}, voided -> /api/v2/voided/{id}, retention -> /api/v2/retentions/{id}, perception -> /api/v2/perceptions/{id}."
          },
          "resourceId": {
            "type": "string",
            "format": "uuid",
            "description": "The resource the operation acts on, in the collection named by resourceKind."
          },
          "operationType": {
            "type": "string",
            "enum": [
              "CREATE_DOCUMENT_V2",
              "SUBMIT_DOCUMENT_V2",
              "GENERATE_PDF_V2",
              "SUBMIT_SUMMARY_V2",
              "SUBMIT_PAYMENT_REGIME_V2"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "RUNNING",
              "SUCCEEDED",
              "FAILED"
            ]
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          },
          "finishedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "ArtifactListResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "documentId",
          "artifacts",
          "representations"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "documentId": {
            "type": "string",
            "format": "uuid"
          },
          "artifacts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ArtifactMetadata"
            }
          },
          "representations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PdfRepresentationMetadata"
            }
          }
        }
      },
      "PaginatedArtifactListResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "documentId",
          "artifacts",
          "representations",
          "representationPage"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "documentId": {
            "type": "string",
            "format": "uuid"
          },
          "artifacts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ArtifactMetadata"
            }
          },
          "representations": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/PdfRepresentationMetadata"
            }
          },
          "representationPage": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "limit",
              "nextCursor"
            ],
            "properties": {
              "limit": {
                "type": "integer",
                "minimum": 1,
                "maximum": 100
              },
              "nextCursor": {
                "type": "string",
                "nullable": true
              }
            }
          }
        }
      },
      "GeneratePdfRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "outputs"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "outputs": {
            "type": "array",
            "minItems": 1,
            "maxItems": 2,
            "items": {
              "oneOf": [
                {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "kind",
                    "templateVersion"
                  ],
                  "properties": {
                    "kind": {
                      "type": "string",
                      "enum": [
                        "pdf-a4"
                      ]
                    },
                    "templateVersion": {
                      "type": "string",
                      "enum": [
                        "invoice-a4-v2"
                      ]
                    }
                  }
                },
                {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "kind",
                    "templateVersion"
                  ],
                  "properties": {
                    "kind": {
                      "type": "string",
                      "enum": [
                        "pdf-ticket"
                      ]
                    },
                    "templateVersion": {
                      "type": "string",
                      "enum": [
                        "invoice-ticket-v1"
                      ]
                    }
                  }
                }
              ]
            }
          }
        }
      },
      "PdfGenerationResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "operationId",
          "resourceId",
          "state",
          "status",
          "replay",
          "representations"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "operationId": {
            "type": "string",
            "format": "uuid"
          },
          "resourceId": {
            "type": "string",
            "format": "uuid"
          },
          "state": {
            "$ref": "#/components/schemas/DocumentState"
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "RUNNING",
              "SUCCEEDED",
              "FAILED"
            ]
          },
          "replay": {
            "type": "boolean"
          },
          "representations": {
            "type": "array",
            "minItems": 1,
            "maxItems": 2,
            "items": {
              "$ref": "#/components/schemas/PdfRepresentationMetadata"
            }
          }
        }
      },
      "PdfRepresentationMetadata": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "representationId",
          "kind",
          "version",
          "status",
          "templateVersion",
          "rendererVersion"
        ],
        "properties": {
          "representationId": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "pdf-a4",
              "pdf-ticket"
            ]
          },
          "version": {
            "type": "integer",
            "minimum": 1,
            "maximum": 2147483647
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "RUNNING",
              "READY",
              "FAILED"
            ]
          },
          "templateVersion": {
            "type": "string",
            "enum": [
              "invoice-a4-v2",
              "invoice-ticket-v1"
            ]
          },
          "rendererVersion": {
            "type": "string",
            "enum": [
              "browser-run-a4-v1",
              "browser-run-ticket-v1"
            ]
          },
          "mediaType": {
            "type": "string",
            "enum": [
              "application/pdf"
            ]
          },
          "byteSize": {
            "type": "integer",
            "minimum": 0,
            "nullable": true
          },
          "sha256": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "nullable": true
          },
          "readyAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "downloadPath": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "ArtifactMetadata": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "kind",
          "status",
          "mediaType",
          "byteSize",
          "sha256",
          "readyAt"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "signed-xml",
              "submission-zip",
              "cdr-zip",
              "cdr-xml",
              "pdf-a4",
              "pdf-ticket",
              "qr-png"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "READY",
              "FAILED"
            ]
          },
          "mediaType": {
            "type": "string",
            "enum": [
              "application/xml",
              "application/zip",
              "application/pdf",
              "image/png"
            ]
          },
          "byteSize": {
            "type": "integer",
            "minimum": 0
          },
          "sha256": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "readyAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "DocumentState": {
        "type": "string",
        "enum": [
          "RECEIVED",
          "VALIDATED",
          "XML_BUILT",
          "SIGNED",
          "SUBMISSION_PENDING",
          "SUBMITTED",
          "TICKET_PENDING",
          "ACCEPTED",
          "OBSERVED",
          "REJECTED",
          "FAILED_RETRYABLE",
          "FAILED_FINAL",
          "AMBIGUOUS",
          "CANCELLED"
        ]
      },
      "DocumentFiscalState": {
        "type": "string",
        "description": "Where a comprobante stands as a fiscal fact, on an axis independent of DocumentState. DocumentState answers \"did the submission reach SUNAT and what did SUNAT say\"; this answers \"does this comprobante still exist\". The normal end state of a successful comunicación de baja is state ACCEPTED with fiscalState VOIDED — the factura was accepted, and then it was withdrawn. ISSUED: nothing has reported or withdrawn it. REPORTED: carried by a resumen diario SUNAT accepted, with catalog 19 status 1 or 2. VOID_REQUESTED: named in a baja that is in flight, or in one whose outcome is unknown because the dispatch ended AMBIGUOUS — it is not a promise that the comprobante will be voided. VOIDED: named in a comunicación de baja SUNAT accepted, or in a resumen diario line with catalog 19 status 3 that SUNAT accepted.",
        "enum": [
          "ISSUED",
          "REPORTED",
          "VOID_REQUESTED",
          "VOIDED"
        ]
      },
      "WebhookEventType": {
        "type": "string",
        "description": "Versioned event catalog. Every entry is a transition the system already commits; document.ambiguous is separate from document.failed on purpose, because an ambiguous SUNAT submission may have arrived and must never be retried by the receiver.",
        "enum": [
          "document.accepted",
          "document.ambiguous",
          "document.failed",
          "document.observed",
          "document.pdf_ready",
          "document.rejected",
          "document.submitted",
          "document.validated"
        ]
      },
      "WebhookDestination": {
        "type": "string",
        "format": "uri",
        "minLength": 12,
        "maxLength": 2000,
        "description": "https URL on the default port, without credentials or a fragment, whose host is a public DNS name. IP literals in any notation, localhost, .local, .internal, .lan, .onion, in-addr.arpa and cloud metadata hosts are refused."
      },
      "CreateWebhookSubscriptionRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "$ref": "#/components/schemas/WebhookDestination"
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "maxItems": 8,
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            }
          },
          "description": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          }
        }
      },
      "UpdateWebhookSubscriptionRequest": {
        "type": "object",
        "additionalProperties": false,
        "minProperties": 1,
        "properties": {
          "url": {
            "$ref": "#/components/schemas/WebhookDestination"
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "maxItems": 8,
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "PAUSED"
            ]
          },
          "description": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "nullable": true
          }
        }
      },
      "WebhookSubscription": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "subscriptionId",
          "url",
          "events",
          "status",
          "description",
          "secretVersion",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "subscriptionId": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "$ref": "#/components/schemas/WebhookDestination"
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "PAUSED"
            ]
          },
          "description": {
            "type": "string",
            "maxLength": 200,
            "nullable": true
          },
          "secretVersion": {
            "type": "integer",
            "minimum": 1
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookSubscriptionResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookSubscription"
          },
          {
            "type": "object",
            "required": [
              "schemaVersion",
              "requestId"
            ],
            "properties": {
              "schemaVersion": {
                "type": "string",
                "enum": [
                  "2.0"
                ]
              },
              "requestId": {
                "type": "string",
                "format": "uuid"
              },
              "resourceId": {
                "type": "string",
                "format": "uuid"
              },
              "replay": {
                "type": "boolean"
              }
            }
          }
        ]
      },
      "WebhookSubscriptionCreatedResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookSubscriptionResponse"
          },
          {
            "type": "object",
            "required": [
              "secret"
            ],
            "properties": {
              "secret": {
                "type": "string",
                "pattern": "^[A-Za-z0-9_-]{43}$",
                "description": "HMAC-SHA256 signing key, 32 random bytes in base64url. Returned only in this response and never retrievable afterwards."
              }
            }
          }
        ]
      },
      "WebhookSubscriptionListResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "subscriptions"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "subscriptions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookSubscription"
            }
          }
        }
      },
      "WebhookSubscriptionDeletedResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "subscriptionId",
          "deleted"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "subscriptionId": {
            "type": "string",
            "format": "uuid"
          },
          "deleted": {
            "type": "boolean",
            "enum": [
              true
            ]
          }
        }
      },
      "WebhookSecretRotationResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "subscriptionId",
          "secretVersion",
          "replay"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "subscriptionId": {
            "type": "string",
            "format": "uuid"
          },
          "secretVersion": {
            "type": "integer",
            "minimum": 2
          },
          "secret": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{43}$",
            "description": "Present only when replay is false."
          },
          "replay": {
            "type": "boolean"
          }
        }
      },
      "EnrollCertificateRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "environment",
          "pfxBase64",
          "pfxPassword",
          "solUser",
          "solPassword"
        ],
        "properties": {
          "environment": {
            "type": "string",
            "enum": [
              "BETA",
              "PRODUCTION"
            ],
            "description": "Must be the environment this company is enrolled for; a mismatch is refused with CERTIFICATE_REJECTED and reason ENVIRONMENT_MISMATCH."
          },
          "pfxBase64": {
            "type": "string",
            "minLength": 4,
            "maxLength": 700000,
            "pattern": "^[A-Za-z0-9+/]+={0,2}$",
            "description": "The PKCS#12 (.pfx/.p12) file, standard base64. Never stored in plaintext, never logged, never returned."
          },
          "pfxPassword": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256,
            "description": "The password that opens the PKCS#12. Verified during this request."
          },
          "solUser": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "pattern": "^[!-~]+$",
            "description": "SUNAT SOL secondary user. Required together with the certificate: the certificate signs the XML and this authenticates sendBill."
          },
          "solPassword": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[!-~]+$"
          },
          "validationMethod": {
            "type": "string",
            "enum": [
              "BETA_AUTHENTICATED",
              "PRODUCTION_AUTHENTICATED",
              "OPERATOR_ATTESTED"
            ],
            "description": "How the SOL credentials were verified. Defaults to OPERATOR_ATTESTED for BETA. A PRODUCTION account requires PRODUCTION_AUTHENTICATED."
          }
        }
      },
      "CertificateVersion": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "version",
          "environment",
          "status",
          "certificateId",
          "sha256",
          "serialNumber",
          "issuerName",
          "subjectRuc",
          "notBefore",
          "notAfter",
          "currentlyValid",
          "revokedAt",
          "validationMethod",
          "createdAt",
          "activatedAt",
          "retiredAt",
          "activation"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The enrolled version. This is the id POST /api/v2/certificates/{id}/activation takes."
          },
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "environment": {
            "type": "string",
            "enum": [
              "BETA",
              "PRODUCTION"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "DRAFT",
              "PENDING_FIRST_ISSUE",
              "ACTIVE",
              "RETIRED"
            ],
            "description": "DRAFT signs nothing. PENDING_FIRST_ISSUE signs everything and is not proved yet: a PRODUCTION version armed through POST /api/v2/certificates/{id}/activation, which the next comprobante SUNAT accepts or observes promotes to ACTIVE. A company can hold at most one version that is ACTIVE or PENDING_FIRST_ISSUE per environment, so the two never compete and an ACTIVE version is never displaced by a newly enrolled one. ACTIVE is what the signing boundary resolves. RETIRED was replaced by a later activation, or was armed and refused by SUNAT for a reason attributable to the certificate; either way it is kept because documents pinned to it must stay verifiable — read activatedAt to tell the two apart, because only the first has one."
          },
          "certificateId": {
            "type": "string",
            "format": "uuid"
          },
          "sha256": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "SHA-256 of the certificate DER, the identity the signer pins."
          },
          "serialNumber": {
            "type": "string",
            "maxLength": 200
          },
          "issuerName": {
            "type": "string",
            "maxLength": 500
          },
          "subjectRuc": {
            "type": "string",
            "pattern": "^[0-9]{11}$"
          },
          "notBefore": {
            "type": "string",
            "format": "date-time"
          },
          "notAfter": {
            "type": "string",
            "format": "date-time"
          },
          "currentlyValid": {
            "type": "boolean",
            "description": "Whether this version could sign right now: inside its validity window and not revoked."
          },
          "revokedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "validationMethod": {
            "type": "string",
            "enum": [
              "BETA_AUTHENTICATED",
              "PRODUCTION_AUTHENTICATED",
              "OPERATOR_ATTESTED"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "activatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "retiredAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "activation": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CertificateVersionActivation"
              }
            ],
            "nullable": true,
            "description": "The latest activation attempt on this version, or null if none was ever requested."
          }
        }
      },
      "CertificateVersionActivation": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "activationId",
          "status",
          "responseCode",
          "responseMessage",
          "failureCode",
          "requestedAt",
          "finishedAt"
        ],
        "properties": {
          "activationId": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "PROBING",
              "ACTIVATED",
              "REJECTED",
              "AMBIGUOUS",
              "FAILED"
            ]
          },
          "responseCode": {
            "type": "string",
            "nullable": true
          },
          "responseMessage": {
            "type": "string",
            "nullable": true
          },
          "failureCode": {
            "type": "string",
            "nullable": true
          },
          "requestedAt": {
            "type": "string",
            "format": "date-time"
          },
          "finishedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "CertificateEnrolledResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "resourceId",
          "id",
          "certificateId",
          "version",
          "environment",
          "status",
          "sha256",
          "serialNumber",
          "issuerName",
          "subjectRuc",
          "notBefore",
          "notAfter",
          "rsaBits",
          "validationMethod",
          "createdAt",
          "activationPath",
          "replay"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "resourceId": {
            "type": "string",
            "format": "uuid"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "certificateId": {
            "type": "string",
            "format": "uuid"
          },
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "environment": {
            "type": "string",
            "enum": [
              "BETA",
              "PRODUCTION"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "DRAFT"
            ],
            "description": "Always DRAFT. Enrolment never changes what signs."
          },
          "sha256": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "serialNumber": {
            "type": "string",
            "maxLength": 200
          },
          "issuerName": {
            "type": "string",
            "maxLength": 500
          },
          "subjectRuc": {
            "type": "string",
            "pattern": "^[0-9]{11}$"
          },
          "notBefore": {
            "type": "string",
            "format": "date-time"
          },
          "notAfter": {
            "type": "string",
            "format": "date-time"
          },
          "rsaBits": {
            "type": "integer",
            "minimum": 0
          },
          "validationMethod": {
            "type": "string",
            "enum": [
              "BETA_AUTHENTICATED",
              "PRODUCTION_AUTHENTICATED",
              "OPERATOR_ATTESTED"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "activationPath": {
            "type": "string",
            "description": "The request that would make this version the one that signs."
          },
          "replay": {
            "type": "boolean",
            "enum": [
              false
            ],
            "description": "Always false on 201. A replayed key answers 200 instead."
          }
        }
      },
      "CertificateEnrollmentReplayResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "resourceId",
          "id",
          "version",
          "environment",
          "status",
          "certificateId",
          "sha256",
          "serialNumber",
          "issuerName",
          "subjectRuc",
          "notBefore",
          "notAfter",
          "currentlyValid",
          "revokedAt",
          "validationMethod",
          "createdAt",
          "activatedAt",
          "retiredAt",
          "activation",
          "activationPath",
          "replay"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "resourceId": {
            "type": "string",
            "format": "uuid"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "environment": {
            "type": "string",
            "enum": [
              "BETA",
              "PRODUCTION"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "DRAFT",
              "PENDING_FIRST_ISSUE",
              "ACTIVE",
              "RETIRED"
            ]
          },
          "certificateId": {
            "type": "string",
            "format": "uuid"
          },
          "sha256": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "serialNumber": {
            "type": "string",
            "maxLength": 200
          },
          "issuerName": {
            "type": "string",
            "maxLength": 500
          },
          "subjectRuc": {
            "type": "string",
            "pattern": "^[0-9]{11}$"
          },
          "notBefore": {
            "type": "string",
            "format": "date-time"
          },
          "notAfter": {
            "type": "string",
            "format": "date-time"
          },
          "currentlyValid": {
            "type": "boolean"
          },
          "revokedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "validationMethod": {
            "type": "string",
            "enum": [
              "BETA_AUTHENTICATED",
              "PRODUCTION_AUTHENTICATED",
              "OPERATOR_ATTESTED"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "activatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "retiredAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "activation": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CertificateVersionActivation"
              }
            ],
            "nullable": true
          },
          "activationPath": {
            "type": "string"
          },
          "replay": {
            "type": "boolean",
            "enum": [
              true
            ]
          }
        }
      },
      "CertificateListResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "certificates"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "certificates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CertificateVersion"
            }
          }
        }
      },
      "CertificateActivationAcceptedResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "mechanism",
          "operationId",
          "activationId",
          "id",
          "resourceId",
          "version",
          "environment",
          "status",
          "certificateStatus",
          "probe",
          "statusPath",
          "createdAt",
          "replay"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "mechanism": {
            "type": "string",
            "enum": [
              "PROBE",
              "FIRST_ISSUE"
            ],
            "description": "WHICH of the two activations was started, and it is the field to branch on before reading anything else. PROBE is BETA: an attempt is recorded and this API is about to sign a factura on the reserved series F000 and send it to SUNAT beta, so there is a verdict coming and statusPath is where it lands. FIRST_ISSUE is PRODUCTION: nothing was sent and nothing will be. The version is armed, it signs from this response onwards, and the FIRST comprobante you issue that SUNAT accepts or observes promotes it to ACTIVE in the same transaction that records that CDR. Production works this way because proving a certificate means having SUNAT accept a document signed with it, and in production such a document is a real comprobante in the taxpayer's registro de ventas, withdrawn only by an accepted comunicación de baja. Issuing one to prove a key is not this platform's decision to make; issuing the one you were going to issue anyway is not a decision at all."
          },
          "operationId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The durable handle for the probe, named operationId because that is what every other deferred write in this API calls its handle. Always equal to activationId. It is NOT a row in the document operation collection — a probe issues no comprobante of the tenant's — so it does not resolve through GET /api/v2/operations/{id}; statusPath is where it is read. NULL when mechanism is FIRST_ISSUE: nothing durable was started, so there is nothing to poll, and publishing a handle that will never move on its own is worse than publishing none."
          },
          "activationId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The certificate version being proved."
          },
          "resourceId": {
            "type": "string",
            "format": "uuid"
          },
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "environment": {
            "type": "string",
            "enum": [
              "BETA",
              "PRODUCTION"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "PENDING_FIRST_ISSUE"
            ],
            "description": "PENDING is the probe's status under mechanism PROBE: the attempt is recorded and nothing has been sent yet. PENDING_FIRST_ISSUE is the certificate VERSION's status under mechanism FIRST_ISSUE, because there is no attempt for it to be the status of."
          },
          "certificateStatus": {
            "type": "string",
            "enum": [
              "DRAFT",
              "PENDING_FIRST_ISSUE"
            ],
            "description": "The version's own status. DRAFT under mechanism PROBE, and that is the point of the 202: requesting activation changes nothing, and whatever signs this company's documents keeps signing them until SUNAT accepts the probe. PENDING_FIRST_ISSUE under mechanism FIRST_ISSUE, and that one DOES change what signs: from this response onwards the company's documents are signed with this version. It is not ACTIVE and must not be read as ACTIVE — a version that signs and has not yet been accepted by SUNAT is precisely what the fourth status exists to say."
          },
          "probe": {
            "type": "object",
            "additionalProperties": false,
            "nullable": true,
            "required": [
              "documentType",
              "series",
              "number"
            ],
            "description": "The comprobante that will be issued against SUNAT beta. It is a real document at SUNAT under this tenant's RUC and it is not a row in this API's document collections. NULL when mechanism is FIRST_ISSUE: no probe exists, and the comprobante that will prove this certificate is one the taxpayer has not written yet.",
            "properties": {
              "documentType": {
                "type": "string",
                "enum": [
                  "01"
                ]
              },
              "series": {
                "type": "string",
                "enum": [
                  "F000"
                ],
                "description": "Reserved for probes. A tenant issuing on F000 is refused with 422 INVALID_DOCUMENT, because its own F000 would collide with a number SUNAT already holds."
              },
              "number": {
                "type": "string",
                "description": "The enrolment version, so a version is probed at most once and no probe number is ever reused."
              }
            }
          },
          "statusPath": {
            "type": "string",
            "description": "Where the verdict is read. Repeated in the Location header."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "replay": {
            "type": "boolean",
            "description": "True when a repeated Idempotency-Key adopted the attempt already recorded, rather than opening a second one."
          }
        }
      },
      "CertificateActivationStatusResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "activationId",
          "id",
          "version",
          "environment",
          "status",
          "certificateStatus",
          "probe",
          "createdAt",
          "finishedAt"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "activationId": {
            "type": "string",
            "format": "uuid"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "environment": {
            "type": "string",
            "enum": [
              "BETA",
              "PRODUCTION"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "PROBING",
              "ACTIVATED",
              "REJECTED",
              "AMBIGUOUS",
              "FAILED"
            ],
            "description": "The probe. PENDING is recorded and not yet dispatched, PROBING is in flight at SUNAT. ACTIVATED means SUNAT accepted the comprobante and the version is now ACTIVE. REJECTED means SUNAT answered and refused it, so the certificate cannot issue and the version stays DRAFT. AMBIGUOUS means the comprobante may have reached SUNAT and no answer came back; it is never redispatched automatically, and the version stays DRAFT. FAILED is a local failure before SUNAT saw anything."
          },
          "certificateStatus": {
            "type": "string",
            "enum": [
              "DRAFT",
              "PENDING_FIRST_ISSUE",
              "ACTIVE",
              "RETIRED"
            ],
            "description": "The version's own status, which is a different question from the probe's. Only status ACTIVATED leaves this ACTIVE; every other terminal probe leaves it DRAFT. PENDING_FIRST_ISSUE never appears beside a probe — it belongs to the PRODUCTION path, which has no attempt — and is listed because this field reports the version and the version has four states."
          },
          "probe": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "documentType",
              "series",
              "number",
              "responseCode",
              "responseMessage",
              "failureCode",
              "probedAt"
            ],
            "properties": {
              "documentType": {
                "type": "string",
                "enum": [
                  "01"
                ]
              },
              "series": {
                "type": "string",
                "enum": [
                  "F000"
                ]
              },
              "number": {
                "type": "string"
              },
              "responseCode": {
                "type": "string",
                "nullable": true,
                "description": "SUNAT's cbc:ResponseCode for the probe, verbatim. Null until SUNAT has answered. This is the field that names which rule a rejected certificate broke."
              },
              "responseMessage": {
                "type": "string",
                "nullable": true,
                "description": "SUNAT's cbc:Description for the probe, verbatim."
              },
              "failureCode": {
                "type": "string",
                "nullable": true,
                "description": "Set instead of a SUNAT code when the probe failed on this side, before or without an answer from SUNAT."
              },
              "probedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "finishedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Null while the probe is PENDING or PROBING."
          }
        }
      },
      "WebhookDeliveryEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "description": "POSTed with content-type application/json. Headers: Apifact-Signature (t=<unix seconds>,v1=<hex hmac-sha256>), Apifact-Timestamp, Apifact-Event-Id, Apifact-Event-Type, Apifact-Schema-Version, Apifact-Subscription-Id, Apifact-Secret-Version, Apifact-Delivery-Id, Apifact-Delivery-Attempt. Verify by recomputing HMAC-SHA256 over the literal 'apifact:webhook:v1' + NUL + the t value + NUL + the RAW body, comparing in constant time, and rejecting a t outside a 300-second window. The attempt counter is a header, not a body field, so the signed bytes are identical on every retry. Deduplicate on eventId. Answer 2xx to acknowledge; any other status, or a redirect, is retried with exponential backoff up to 12 attempts and then dead-lettered.",
        "required": [
          "schemaVersion",
          "eventId",
          "eventType",
          "occurredAt",
          "subscriptionId",
          "deliveryId",
          "data"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "pattern": "^[0-9]+\\.[0-9]+$",
            "description": "Payload version of this event type, currently 1.0 for the whole catalog. Refuse a major version you do not implement rather than guessing."
          },
          "eventId": {
            "type": "string",
            "format": "uuid"
          },
          "eventType": {
            "$ref": "#/components/schemas/WebhookEventType"
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time"
          },
          "subscriptionId": {
            "type": "string",
            "format": "uuid"
          },
          "deliveryId": {
            "type": "string",
            "format": "uuid"
          },
          "data": {
            "type": "object",
            "description": "Identifiers and state only. Never the canonical payload, the signed XML, the CDR, customer data or any amount: a webhook notifies that something happened to a document the caller already owns, it is not a second unauthenticated read API.",
            "additionalProperties": true,
            "properties": {
              "documentId": {
                "type": "string",
                "format": "uuid"
              },
              "operationId": {
                "type": "string",
                "format": "uuid"
              },
              "documentType": {
                "type": "string",
                "enum": [
                  "01",
                  "03",
                  "07",
                  "08"
                ]
              },
              "series": {
                "type": "string"
              },
              "number": {
                "type": "string"
              },
              "state": {
                "$ref": "#/components/schemas/DocumentState"
              },
              "previousState": {
                "type": "string",
                "nullable": true
              },
              "stateVersion": {
                "type": "integer",
                "minimum": 1
              },
              "representationId": {
                "type": "string",
                "format": "uuid"
              },
              "kind": {
                "type": "string",
                "enum": [
                  "pdf-a4",
                  "pdf-ticket"
                ]
              },
              "version": {
                "type": "integer",
                "minimum": 1
              }
            }
          }
        }
      },
      "TaxpayerConsultationResponse": {
        "type": "object",
        "additionalProperties": false,
        "description": "Informational only. A registered=false answer does not prevent, discourage or flag a comprobante issued to this RUC; nothing on the issuing path reads this catalogue.",
        "required": [
          "schemaVersion",
          "requestId",
          "ruc",
          "registered",
          "registry",
          "taxpayer"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "ruc": {
            "type": "string",
            "pattern": "^[0-9]{11}$",
            "description": "The RUC that was asked about, echoed back."
          },
          "registered": {
            "type": "boolean",
            "description": "Whether this RUC is present in the snapshot named by `registry`. FALSE IS A REAL, SUCCESSFUL ANSWER and means the snapshot was consulted and does not contain it — not that this API could not answer, which is 503 TAXPAYER_REGISTRY_UNAVAILABLE. Because the snapshot is always behind, false also does not mean the RUC cannot be invoiced."
          },
          "registry": {
            "type": "object",
            "additionalProperties": false,
            "description": "Which snapshot answered. Present on both outcomes, so the age of a negative answer is as readable as the age of a positive one.",
            "required": [
              "publishedOn",
              "rowCount"
            ],
            "properties": {
              "publishedOn": {
                "type": "string",
                "format": "date",
                "description": "SUNAT's own publication date for the loaded snapshot, never the time this API ingested it. This is how old the answer is."
              },
              "rowCount": {
                "type": "integer",
                "minimum": 0,
                "description": "How many taxpayers the serving snapshot holds."
              }
            }
          },
          "taxpayer": {
            "type": "object",
            "nullable": true,
            "additionalProperties": false,
            "description": "Null exactly when registered is false.",
            "required": [
              "ruc",
              "legalName",
              "status",
              "statusClass",
              "condition",
              "conditionClass"
            ],
            "properties": {
              "ruc": {
                "type": "string",
                "pattern": "^[0-9]{11}$"
              },
              "legalName": {
                "type": "string",
                "description": "SUNAT's razón social or nombre comercial as published."
              },
              "status": {
                "type": "string",
                "description": "SUNAT's literal ESTADO DEL CONTRIBUYENTE, verbatim and authoritative. SUNAT truncates these to 20 characters in the padrón reducido, so values such as 'BAJA PROV. POR OFICI' arrive without their closing letters; that is the published value, not a defect. Match on statusClass rather than on this string unless you need the exact wording."
              },
              "statusClass": {
                "type": "string",
                "enum": [
                  "ACTIVE",
                  "INACTIVE",
                  "SUSPENDED",
                  "PENDING",
                  "OTHER",
                  "UNKNOWN"
                ],
                "description": "This API's coarse classification of `status`, so an integrator does not have to compare Spanish strings. UNKNOWN means SUNAT published a value this API does not map — it is never a guess, and the literal beside it is what to fall back on. OTHER covers real SUNAT states that are neither alive nor withdrawn, such as NUM. INTERNO IDENTIF and OTROS OBLIGADOS."
              },
              "condition": {
                "type": "string",
                "description": "SUNAT's literal CONDICIÓN DE DOMICILIO, verbatim and authoritative, truncated to 20 characters by SUNAT in the same way as `status`."
              },
              "conditionClass": {
                "type": "string",
                "enum": [
                  "HABIDO",
                  "NO_HABIDO",
                  "NO_HALLADO",
                  "NOT_APPLICABLE",
                  "PENDING",
                  "UNKNOWN"
                ],
                "description": "This API's coarse classification of `condition`. NO_HALLADO and NO_HABIDO are different findings and are kept apart. UNKNOWN means SUNAT published a value this API does not map, including the literal '-' that many rows carry, and is never reported as HABIDO."
              }
            }
          }
        }
      },
      "ExchangeRateConsultationResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "currency",
          "requestedOn",
          "rateDate",
          "buyRate",
          "sellRate",
          "source",
          "fallback",
          "stalenessDays"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "currency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$",
            "description": "The requested currency, upper-cased. USD is the only series loaded."
          },
          "requestedOn": {
            "type": "string",
            "format": "date",
            "description": "The date that was asked about, echoed back. Never a server-side 'today'."
          },
          "rateDate": {
            "type": "string",
            "format": "date",
            "description": "The date this rate was actually PUBLISHED FOR. Equal to requestedOn on a publication day; earlier when the último-publicado rule carried an earlier publication forward. Never later than requestedOn: the resolution never looks ahead, so a Sunday resolves back to Friday and not forward to Monday."
          },
          "buyRate": {
            "type": "string",
            "pattern": "^[0-9]+\\.[0-9]{6}$",
            "description": "Tipo de cambio compra, six decimals. A STRING, to be parsed as a decimal: this number gets multiplied into money and a binary float is where it would change value. Not the IGV answer — it is here because the Impuesto a la Renta rule splits compra/venta by activo/pasivo."
          },
          "sellRate": {
            "type": "string",
            "pattern": "^[0-9]+\\.[0-9]{6}$",
            "description": "Tipo de cambio promedio ponderado venta, six decimals, as a string for the same reason as buyRate. THIS IS THE IGV ANSWER: D.S. 29-94-EF artículo 5 numeral 17 names the venta rate flatly, for sales and purchases alike."
          },
          "source": {
            "type": "string",
            "pattern": "^[A-Z][A-Z0-9_]*$",
            "description": "Who published the series this row came from, e.g. SBS. Recorded so a hand-entered row and a published one stay distinguishable."
          },
          "fallback": {
            "type": "boolean",
            "description": "True when rateDate differs from requestedOn, i.e. the answer was carried forward under 'el último publicado' because the requested date had no publication. Weekends and holidays make this the normal case, not an error. A caller that displays the rate must display rateDate with it when this is true."
          },
          "stalenessDays": {
            "type": "integer",
            "minimum": 0,
            "description": "Calendar days between rateDate and requestedOn; 0 when fallback is false. Published because it is the only way to tell an ordinary weekend (2, or 3 over a Monday holiday) from a loader that stopped running (40). This API imposes NO ceiling — the norm has none, and refusing past an invented threshold would refuse an answer the norm considers correct — so any threshold is the caller's to choose."
          }
        }
      },
      "DniConsultationResponse": {
        "type": "object",
        "additionalProperties": false,
        "description": "Informational only. Nothing on the issuing path resolves a DNI: a boleta whose acquirer name was typed by hand is issued exactly as before, and a 502 or 503 from this route does not affect document issuance. THERE ARE TWO ANSWERS AND SEVEN NON-ANSWERS. found=true and found=false are both 200 and are both statements about the person; every failure to obtain an answer is a 502 (the directory answered something unusable) or a 503 (the directory produced no answer), each with its own error code, and NONE of them may be read as found=false. A client that folds DNI_DIRECTORY_UNAVAILABLE into 'this person does not exist' turns an outage of ours into a refused customer.",
        "required": [
          "schemaVersion",
          "requestId",
          "dni",
          "found",
          "consultedAt",
          "person"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "dni": {
            "type": "string",
            "pattern": "^[0-9]{8}$",
            "description": "The DNI that was asked about, echoed from the request path and never from the directory's own response."
          },
          "found": {
            "type": "boolean",
            "description": "Whether the registry holds a record for this DNI. FALSE IS A REAL, SUCCESSFUL ANSWER: the directory was reached, answered, and has no such record. It is not the same as this API being unable to ask, which is never a 200."
          },
          "consultedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When this answer was obtained. This surface never serves a stored answer — there is no cache — so this is always the instant of a live consultation, and it is the instant to record beside any name you keep."
          },
          "person": {
            "type": "object",
            "nullable": true,
            "additionalProperties": false,
            "description": "Null exactly when found is false. NAMES ONLY: this API requests the directory's minimal endpoint, so no birth date and no gender are ever obtained, transported or available — a boleta needs neither, and Ley 29733's minimisation principle makes asking for them the wrong default rather than a missing feature.",
            "required": [
              "fullName",
              "firstNames",
              "paternalSurname",
              "maternalSurname",
              "verificationDigit"
            ],
            "properties": {
              "fullName": {
                "type": "string",
                "description": "The registry's own composition of the full name, preferred over one assembled from the parts: the order of two surnames is RENIEC's to decide, not this API's."
              },
              "firstNames": {
                "type": "string"
              },
              "paternalSurname": {
                "type": "string"
              },
              "maternalSurname": {
                "type": "string"
              },
              "verificationDigit": {
                "type": "string",
                "nullable": true,
                "description": "RENIEC's codigo_verificacion: a check digit of the DNI that was supplied, not a further fact about the person. Null when the directory omitted it, which never costs the caller the name."
              }
            }
          }
        }
      },
      "ErrorEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "requestId",
          "error"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "requestId": {
            "type": "string",
            "format": "uuid"
          },
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "details": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          }
        }
      }
    }
  }
}
