{
  "openapi": "3.1.0",
  "info": {
    "title": "Facturia API",
    "version": "1.0.0",
    "summary": "Multi-tenant Chilean electronic invoicing (DTE), reception and tax automation.",
    "description": "The machine-readable contract for the Facturia v1 public surface. The prose specification —\nwith rationale, curl examples and the estado mapping tables — lives in\n`public-api-spec.md` alongside this file and is the normative document where the two differ.\n\nPUBLISHED AT https://docs.facturia.cl/openapi.yaml, and as JSON at\nhttps://docs.facturia.cl/openapi.json. The JSON is generated from this exact YAML at build\ntime, so the two cannot drift; either is codegen-ready.\n\n`info.version` is the version of THIS DOCUMENT, not a release channel you can select.\nIt moves by semver against the surface it describes: PATCH for wording, MINOR when paths,\noperations or fields are ADDED — which is the only kind of change `/v1` makes — and MAJOR only\never alongside the twelve-month withdrawal notice of `public-api-spec.md` §5.8. So a client\ngenerated from `1.x` keeps compiling against every later `1.y`.\n\nLANGUAGE, ONCE, FOR THE WHOLE DOCUMENT (#512). Two languages, on purpose, and which one a\nfield carries is a property of WHO READS IT:\n\n  * ENGLISH — everything a program consumes: field names, enum values, `error.code`,\n    `error.message` and `error.hint`. Machine text. Translate it if you show it to a person;\n    do not branch on it (branch on `code`).\n  * CHILEAN SPANISH — every string written for the PERSON WHO PERFORMS AN ACT, meant to be\n    relayed VERBATIM to them: `pending_action.*` (`what`, `where`, `instructions[]`,\n    `bring[]`, `how_we_know`), `Onboarding.steps[].label` and `.help`,\n    `Onboarding.requirements[].label`, `blocked_reason.message` and `blocked_reason.hint`,\n    `OnboardingRecheck.message`,\n    `Mandato` display text, and every `message` on the `/account/*` bootstrap surface. These\n    are not error strings: they are the words a human is supposed to read on a screen or hear\n    from an agent, and a translation of them is a worse translation than ours. Relay them as\n    they arrive.\n\nA field whose description says «Spanish.» is in the second group. Anything unmarked is in the\nfirst.\n\nConventions:\n  * Amounts are CLP integers. Unit prices may carry up to 4 decimals.\n  * Civil dates (`issue_date`, `period`) are America/Santiago. Timestamps are RFC 3339 UTC.\n  * RUTs are canonical `########-D` (no dots, uppercase K).\n  * Lists are cursor-paginated and share the `List` envelope.\n  * Every empresa-scoped call carries the `Facturia-Empresa` header, unless the key is an\n    empresa token (a key whose allowlist holds exactly one empresa). The header is the ONLY\n    mechanism — there is no `?empresa=` query parameter and no `empresa` body field.\n  * The key prefix decides the mode: `sk_test_` = fully mocked sandbox, `sk_live_` = real SII.\n  * Rate limits are published starting limits, applied per key, and raised on request.\n  * `/v1` is the whole version contract and evolves additively. There is no dated-version\n    header in v1; a Stripe-style dated release channel is the v2 mechanism if one is ever needed.\n  * Documentation: https://docs.facturia.cl\n\nWHERE THE FIRST KEY COMES FROM. Every operation here authenticates with an `sk_` key, and a\nbrand-new account has none. Keys are minted from the BOOTSTRAP SURFACE, which has its own\nmachine contract because it cannot honestly share this one: it answers outside `/v1`, on a\ndifferent base URL, with an account JWT rather than a key, and — on `/account/*` — in a\ndifferent error envelope. A single document's `servers` and `security` apply to every path in\nit, so folding the two together would declare URLs and credentials that do not exist.\n\nSo it is published alongside, and it is a complete OpenAPI 3.1 document of its own:\n\n    https://docs.facturia.cl/openapi-bootstrap.yaml\n    https://docs.facturia.cl/openapi-bootstrap.json\n\nGenerate a client from THAT one first. The flow it describes is `POST /account/register`\n(or `/account/login`) for a 1-hour session token, then `POST /dashboard/api_keys` with that\ntoken and a `Facturia-Mode: test|live` header, which returns the `sk_test_` key every\noperation below expects. Prose: `public-api-spec.md` §3.7.\n\nRESPONSE HEADERS, on every operation below unless noted:\n  * `Facturia-Request-Id` — `req_…`, on every response, success or failure. Quote it in support.\n  * `RateLimit-Limit` / `-Remaining` / `-Reset` — on every AUTHENTICATED response, reporting the\n    tightest applicable bucket. Absent from the unauthenticated `Meta` operations.\n  * `Retry-After` — on `429`, on `409 idempotency_in_progress`, and on every other refusal\n    whose `retriable` is `true`. A refusal that says «come back» now always says when: the\n    same number also travels in the body as `error.retry_after_seconds`, for a consumer that\n    stored the JSON and no longer has the headers.\n  * `Facturia-Idempotent-Replay: true` — on a replayed `POST`, alongside the original body.\n  * `Facturia-Poll-After` — seconds until the next USEFUL poll: on `GET /documents/{document_id}`,\n    on the `201` of `POST /documents` (the response that answers `queued`, so the cadence is\n    there without a first read), on `GET /jobs/{job_id}`, and on any retriable refusal\n    alongside `Retry-After`. Absent once a document or job is terminal. On a job the cadence is\n    a function of `type`: what a job waits on differs by an order of magnitude, and the floor\n    under all of them is the queue's own 10-second claim interval.\n`Facturia-Mode` is a REQUEST header, and only on the bootstrap surface above.\n`Facturia-Delivery-Id` is a header we SEND on an outbound webhook delivery (see `webhooks`).\n\nNested DTE detail (item taxes, dispatch variants, reference codes) is modelled\nrepresentatively rather than exhaustively; the prose spec and the error dictionary at\n`GET /error_codes` are authoritative for edge cases.\n",
    "termsOfService": "https://facturia.cl/legal/terminos",
    "contact": {
      "name": "Facturia developer support",
      "url": "https://docs.facturia.cl",
      "email": "dev@facturia.cl"
    },
    "license": {
      "name": "Documentation licensed CC BY 4.0",
      "url": "https://creativecommons.org/licenses/by/4.0/"
    }
  },
  "servers": [
    {
      "url": "https://api.facturia.cl/v1",
      "description": "Production. The key prefix (sk_test_ / sk_live_) selects sandbox or live behaviour."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Account",
      "description": "The authenticated account and its keys."
    },
    {
      "name": "Empresas",
      "description": "Tenants. Creation, certificate custody, mandato, onboarding, automation settings."
    },
    {
      "name": "Folios",
      "description": "CAF and folio availability per doc type."
    },
    {
      "name": "Documents",
      "description": "Emission of DTEs (33/34/39/41/46/52/56/61) and their artifacts."
    },
    {
      "name": "Received documents",
      "description": "Supplier DTEs and the respuesta comercial (aceptación / reclamo)."
    },
    {
      "name": "Tax",
      "description": "RCV, caracterización, F29 propuesta and filing, tipo-46 suggestions."
    },
    {
      "name": "Connectors",
      "description": "First-party sources that feed the expense ledger: the Fintoc bank/card movement feed and\ncartola (CSV/XLSX) upload. Ad-platform billing connectors (Meta, Google Ads) are deferred\npast v1 — Fintoc and cartola catch the same charges from the paying end.\n"
    },
    {
      "name": "Webhooks",
      "description": "Endpoint management, event ledger, delivery inspection and replay."
    },
    {
      "name": "Usage",
      "description": "The immutable usage ledger. Reporting, not a meter — the plan is flat and unlimited."
    },
    {
      "name": "Test helpers",
      "description": "Test-mode-only endpoints that drive the outside world."
    },
    {
      "name": "Meta",
      "description": "Unauthenticated reference data."
    }
  ],
  "paths": {
    "/account": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getAccount",
        "summary": "Retrieve the account and the key in use",
        "description": "The authenticated account and the credential in use — email, plan, empresas_ready/total, and the calling key's own scopes, mode and allowlist.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                },
                "examples": {
                  "default": {
                    "summary": "An account with one empresa, all set up.",
                    "value": {
                      "object": "account",
                      "id": "acct_01K2QW7X9M5P3R",
                      "name": "CONNECT SpA",
                      "key": {
                        "id": "key_01K2QWB7T4N9",
                        "object": "api_key",
                        "name": "Servidor de producción",
                        "mode": "live",
                        "root": false,
                        "last4": "aB3d",
                        "scopes": [
                          "documents:read",
                          "documents:write"
                        ],
                        "empresas": [
                          "77928532-4"
                        ],
                        "created_at": "2026-06-21T14:02:00Z",
                        "last_used_at": "2026-09-02T11:47:00Z"
                      },
                      "empresas_ready": 1,
                      "empresas_total": 1,
                      "limits": {
                        "requests_per_minute": 300,
                        "emissions_per_second": 10
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`empresa_header_forbidden` — this operation is account-level; a `Facturia-Empresa`\nheader on it is refused rather than silently ignored.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/api_keys": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "listApiKeys",
        "summary": "List API keys (metadata only; secrets are never returned)",
        "description": "A root key sees every key on the account. An ordinary key sees only itself.\n",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A list of API keys.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ApiKey"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "post": {
        "tags": [
          "Account"
        ],
        "operationId": "createApiKey",
        "summary": "Mint an API key (root key only)",
        "description": "Requires a root key (`sk_live_root_…` / `sk_test_root_…`) carrying `keys:write`.\nGuardrails, enforced server-side: the new key's `empresas` must be a subset of the\nminter's allowlist and its `scopes` a subset of the minter's scopes (violations return\n`403 privilege_escalation`); mode is inherited from the minter; root keys cannot mint\nroot keys. The `secret` is returned exactly once.\n",
        "security": [
          {
            "bearerAuth": [
              "keys:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "scopes"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Scope"
                    }
                  },
                  "empresas": {
                    "type": "array",
                    "description": "Subset of the minting key's allowlist. A single RUT makes it an empresa token.",
                    "items": {
                      "$ref": "#/components/schemas/Rut"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The minted key, including its secret.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyWithSecret"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/api_keys/{key_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/KeyId"
        }
      ],
      "delete": {
        "tags": [
          "Account"
        ],
        "operationId": "revokeApiKey",
        "summary": "Revoke an API key immediately (root key only)",
        "description": "Revoke a key immediately and irreversibly. Requires a root key or an account session (`403 root_key_required`) — an ordinary key cannot revoke keys, itself included.",
        "security": [
          {
            "bearerAuth": [
              "keys:write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked. Revocation is immediate and unrecoverable."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/api_keys/{key_id}/roll": {
      "parameters": [
        {
          "$ref": "#/components/parameters/KeyId"
        }
      ],
      "post": {
        "tags": [
          "Account"
        ],
        "operationId": "rollApiKey",
        "summary": "Issue a replacement secret for a key (root key only)",
        "description": "The previous secret keeps working for 24 hours so you can deploy without downtime.\n\nThe roll is a compare-and-swap on the key's current hash, so two rolls of the\nsame key can never both land: exactly one wins and the loser answers\n`409 api_key_roll_conflict`, whose secret is the live one. Roll once per key.\n",
        "security": [
          {
            "bearerAuth": [
              "keys:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The key with its new secret.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyWithSecret"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas": {
      "get": {
        "tags": [
          "Empresas"
        ],
        "operationId": "listEmpresas",
        "summary": "List empresas visible to this key",
        "description": "List every empresa this key can reach — its own allowlist resolved to real rows, or the whole account for an unrestricted key.",
        "security": [
          {
            "bearerAuth": [
              "empresas:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/EmpresaStatus"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of empresas.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Empresa"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "post": {
        "tags": [
          "Empresas"
        ],
        "operationId": "createEmpresa",
        "summary": "Create an empresa",
        "description": "In test mode the empresa is born `emitting`: no mandato, no certificate, no certification.\nIn live mode it starts at `mandato_pending` and progresses through the onboarding state machine.\n\n**`origen` IS REQUIRED IN LIVE MODE** and this operation refuses without it —\n`400 invalid_field` with `param: \"origen\"` (#495). It is not in `required` above because\nthe mode is a property of the CREDENTIAL, not of the body, and a schema cannot say «required\nwhen the bearer token starts with `sk_live_`»: declaring it unconditionally would make every\nsandbox call — where it defaults to `ninguno` — fail a generated client's own validation\nbefore it left the machine. So the rule lives on the `400` below, where a codegen'd client\ncan at least see it, and in the field's own description.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmpresaCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created empresa.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Empresa"
                }
              }
            }
          },
          "400": {
            "description": "Malformed input. The codes this operation answers:\n\n  * `invalid_rut` — `rut` missing, or its check digit is wrong.\n  * `invalid_field` with `param: \"legal_name\"` — absent or empty.\n  * `invalid_field` with `param: \"origen\"` — **absent, on a live key.** The first\n    empresa of every real customer hits this if the caller treated the field as\n    optional because the schema does. See the operation description.\n  * `invalid_field` with `param: \"doc_types\"` — not an array of supported tipos, or\n    an explicit `[]`.\n  * `invalid_field` with `param: \"emission_channel\"` / `\"acteco\"` /\n    `\"codigo_sucursal\"` — a value outside what the field admits.\n  * `invalid_field` with `param: \"ambiente\"` — `ambiente` is derived and is refused\n    rather than ignored.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "description": "`doc_type_not_supported_on_channel` — `doc_types` names a tipo the resolved\n`emission_channel` cannot build (39/41 on `sfg`). `error.param` is `doc_types`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        }
      ],
      "get": {
        "tags": [
          "Empresas"
        ],
        "operationId": "getEmpresa",
        "summary": "Retrieve an empresa",
        "description": "One empresa: legal identity, `ambiente`, `emission_channel`/`certification_track`, onboarding spine and `capabilities`.",
        "security": [
          {
            "bearerAuth": [
              "empresas:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The empresa.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Empresa"
                },
                "examples": {
                  "default": {
                    "summary": "An empresa emitting live on the mercado channel.",
                    "value": {
                      "object": "empresa",
                      "id": "emp_01K2R6ZC4P8QW1VN7T3MHDY9EK",
                      "mode": "live",
                      "rut": "77928532-4",
                      "legal_name": "CONNECT SpA",
                      "giro": "Servicios de tecnología",
                      "acteco": [
                        620200
                      ],
                      "address": {
                        "street": "Av. Providencia 1208, of. 504",
                        "comuna": "Providencia",
                        "city": "Santiago"
                      },
                      "codigo_sucursal": null,
                      "contact_email": "contacto@connect.cl",
                      "ambiente": "produccion",
                      "emission_channel": "mercado",
                      "certification_track": "mercado",
                      "doc_types": [
                        33,
                        34,
                        39,
                        41,
                        46,
                        52,
                        56,
                        61
                      ],
                      "origen": "mercado",
                      "reception_email": "77928532-4@dte.facturia.cl",
                      "status": "active",
                      "iva_shape": null,
                      "onboarding": {
                        "status": "emitting",
                        "progress": 1,
                        "blocked_on": null,
                        "next_action_url": null,
                        "url": "https://api.facturia.cl/v1/empresas/emp_01K2R6ZC4P8QW1VN7T3MHDY9EK/onboarding"
                      },
                      "capabilities": {
                        "can_emit": true,
                        "tipos": [
                          33,
                          34,
                          39,
                          41,
                          46,
                          52,
                          56,
                          61
                        ],
                        "blocked_by": null,
                        "next": {
                          "who": "agent",
                          "api": "POST /v1/documents",
                          "url": null
                        }
                      },
                      "created_at": "2026-06-21T09:15:00Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "patch": {
        "tags": [
          "Empresas"
        ],
        "operationId": "updateEmpresa",
        "summary": "Update an empresa",
        "description": "Update `legal_name`, `giro`, `contact_email`, `acteco`, `origen`, `codigo_sucursal`,\n`doc_types`, `emission_channel`, `certification_track` or `status`. `ambiente` is\nderived and rejected if sent (`422`); `origen` is the titular's own claim about how the\nempresa emits today, correctable, never a canal preference.\n\nTwo of these move the onboarding machine and say so in their own descriptions:\n`doc_types` (adding a tipo re-opens certification for it; 39/41 need the `mercado`\nchannel) and `certification_track` (which checklist governs the empresa).\n\nTHE PANEL MIRROR IS NARROWER. `PATCH /dashboard/empresas/{empresa_id}` — the same\nhandler behind an account session — accepts only `doc_types` and `certification_track`,\nand refuses every other field with `400 invalid_field` naming it. Those two are what the\npuesta en marcha wizard has to record; the rest are the taxpayer identity each document\nis printed from, and a browser session is not the credential that rewrites what the SII\nsees on an invoice. An `sk_` key on THIS path is unaffected.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmpresaUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated empresa.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Empresa"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "delete": {
        "tags": [
          "Empresas"
        ],
        "operationId": "deleteEmpresa",
        "summary": "Delete a SANDBOX empresa",
        "description": "Throw away a scratch empresa. **Sandbox only.**\n\nA sandbox empresa holds nothing that is a tax record: its documents are mocked, it never\nreached the SII, and it reserves nothing — not the RUT (uniqueness is per mode) and not the\n`<rut>@dte.facturia.cl` reception alias, which live empresas alone carry. So it can be\ndeleted, and the empresa plus everything hanging off it goes in one transaction.\n\nA LIVE empresa is `409 empresa_delete_not_allowed`, always. §7.1: emitted documents are tax\nrecords. The two real exits are `DELETE /v1/empresas/{empresa_id}/mandato` (ends the\nauthorization, cláusula 7.1.3) and `PATCH /v1/empresas/{empresa_id}` with\n`{\"status\": \"archived\"}` (stops emission and billing); both keep the history.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted. No body."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`empresa_delete_not_allowed` — the empresa is live. Revoke the mandato and/or archive it.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/certificate": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        }
      ],
      "get": {
        "tags": [
          "Empresas"
        ],
        "operationId": "getCertificate",
        "summary": "Retrieve certificate metadata",
        "description": "Metadata only. The .pfx and its password are never readable through the API.",
        "security": [
          {
            "bearerAuth": [
              "empresas:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Certificate metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Certificate"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "post": {
        "tags": [
          "Empresas"
        ],
        "operationId": "uploadCertificate",
        "summary": "Upload (or reuse) the empresa's digital certificate",
        "description": "Upload the empresa's `.pfx` (`multipart/form-data` with `file` and `password`) or reuse another empresa's valid certificate (`reuse_from_empresa`). An expired certificate is refused at ingest.",
        "security": [
          {
            "bearerAuth": [
              "onboarding:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "PKCS#12 (.pfx) file."
                  },
                  "password": {
                    "type": "string",
                    "description": "The .pfx password."
                  },
                  "reuse_from_empresa": {
                    "type": "string",
                    "description": "Instead of uploading, reference a certificate already held for another empresa\non this account. Valid only when that certificate is a SII-authorized user of\nthis empresa's RUT.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Certificate stored and verified.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Certificate"
                    },
                    {
                      "type": "object",
                      "description": "WHAT FOLLOWS, ON THE RECEIPT (#536, extended to this door by the\n2026-09-04 verification pass). The upload enqueues the certificate\nverification and the permisos probe, so the checklist is about to move;\nthese two fields say where to look and whether anything is waiting on a\nperson right now.\n",
                      "properties": {
                        "next": {
                          "allOf": [
                            {
                              "$ref": "#/components/schemas/ApiCall"
                            }
                          ],
                          "description": "The onboarding read that reflects this upload once the probes run."
                        },
                        "pending_action": {
                          "oneOf": [
                            {
                              "$ref": "#/components/schemas/PendingAction"
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "The handoff of whatever the checklist is waiting on AS OF THIS RESPONSE —\nthe same object `GET .../onboarding` publishes, from the same builder.\n`null` when the next step is ours, which is different from \"nothing left\".\n"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "delete": {
        "tags": [
          "Empresas"
        ],
        "operationId": "deleteCertificate",
        "summary": "Purge the certificate from the vault (revokes custody, suspends emission)",
        "description": "Remove the stored certificate metadata. The empresa returns to needing one before it can sign anything the SII will accept.",
        "security": [
          {
            "bearerAuth": [
              "empresas:write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Purged."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/sfg_credential": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        }
      ],
      "get": {
        "tags": [
          "Empresas"
        ],
        "operationId": "getSfgCredential",
        "summary": "Retrieve the SFG centralizado clave's custody metadata",
        "description": "Metadata only — whether we hold the clave and when it was stored. The value itself is\nnever readable through the API: there is no code path in this endpoint that decrypts it.\n\nOnly an `emission_channel: sfg` empresa in live mode has one. A mercado empresa signs\nwith the certificate in custody and never asks the SII to sign for it, and a test-mode\nempresa has no Portal MIPYME at all; both answer `422` naming the fix.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The custody record.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SfgCredential"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "post": {
        "tags": [
          "Empresas"
        ],
        "operationId": "setSfgCredential",
        "summary": "Store (or replace) the SFG centralizado clave",
        "description": "The value goes straight to the per-tenant vault and its id to the empresa's credential\nrow; there is no plaintext column. Replacing destroys the outgoing secret first, so the\ndestruction is in the custody record rather than a side effect nobody can point at.\n\nThe response is the same metadata `GET` returns. The clave is not echoed back.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SfgCredentialCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Stored.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SfgCredential"
                    },
                    {
                      "type": "object",
                      "description": "The same three fields the certificate door publishes (#529, #536): the\nclave is in the vault, a tick was queued to fold it into the checklist,\nand this is what the checklist is waiting on now.\n",
                      "properties": {
                        "queued": {
                          "type": "boolean",
                          "description": "Whether the onboarding tick that folds the new fact was enqueued."
                        },
                        "next": {
                          "allOf": [
                            {
                              "$ref": "#/components/schemas/ApiCall"
                            }
                          ],
                          "description": "The onboarding read that reflects this save."
                        },
                        "pending_action": {
                          "oneOf": [
                            {
                              "$ref": "#/components/schemas/PendingAction"
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "The next human handoff, or `null` when the following step is ours.\n"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "delete": {
        "tags": [
          "Empresas"
        ],
        "operationId": "deleteSfgCredential",
        "summary": "Destroy the SFG centralizado clave (ends custody)",
        "description": "Destroys the bytes. `204` even when there was nothing to destroy — you asked for a state\n(\"you hold nothing of mine\"), and that state is the outcome either way.\n\nUnlike `DELETE .../certificate` it does NOT walk the empresa's onboarding back: the next\nSFG emission refuses for want of a signing clave, which the emit path already reports\nhonestly, and a clave can be re-supplied in one call.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Destroyed."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/mandato": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        }
      ],
      "get": {
        "tags": [
          "Empresas"
        ],
        "operationId": "getMandato",
        "summary": "Whether the representante legal has signed, against which terms, and how",
        "description": "The read half of the hosted mode. Mint a `signing_url` with an empty `POST`, send it to\nthe representante legal, then poll this - completion is observable by the agent that\nasked for it, without subscribing to a webhook or inferring it from `steps[].status`.\n\nTwo fields answer questions the onboarding checklist cannot. `terms_current: false` on a\n`signed` mandato means the text has moved on and the pipeline has already stopped\ntrusting the acceptance - `POST` again to collect a fresh one. And `acceptance_method`\ndistinguishes an *aprobacion* (`hosted`: a human of the required role completed a page\nthat showed them what they were signing) from an *atestacion* (`api`: an integrator's\nclaim that one did). Those are different pieces of evidence.\n\n`signing_url` is ALWAYS `null` here: only a SHA-256 of the token is stored, so no read\ncan reproduce it. `POST` with an empty body re-mints, revoking the previous link.\n\n`status: \"none\"` for an empresa nobody has asked yet. It is a state, not a missing\nresource - the empresa exists and you are authorized for it.\n\nScope: `mandato:write` (which `empresas:write` implies). The scope that mints the link\nis the scope that reads whether it was used.\n",
        "security": [
          {
            "bearerAuth": [
              "mandato:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The mandato, or a `none` status when nobody has been asked yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Mandato"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "post": {
        "tags": [
          "Empresas"
        ],
        "operationId": "createMandato",
        "summary": "Record the mandato authorizing Facturia to act before the SII",
        "description": "Post the acceptance you collected yourself, or omit the body to receive a hosted\n`signing_url` to send the representante legal.\n\nWe record that an acceptance happened, by whom, from where, and against which exact\nbytes of the mandato text. We do NOT verify a firma electrónica avanzada: whether one\nis required is an open legal question, and until it is answered this endpoint claims\nno more than the evidence it holds.\n\nWhile `GET /v1/mandato_terms` reports `status: \"draft\"`, a live-mode acceptance is\nrefused with `422 mandato_terms_draft` — the same warning §7.3 prints, enforced.\n\n**Two forms, two scopes (ADR-0012 §4).** No body — mint the enlace de firma for the\nrepresentante legal — needs `mandato:write`, which `empresas:write` implies. A body —\nasserting a signature you collected in your own flow — is an *atestación* and needs\n`mandato:attest` **itself**: `empresas:write` does NOT imply it (corrected 2026-09-03), so\na key that used to post a body under `empresas:write` alone now gets\n`403 insufficient_scope` naming `mandato:attest`.\n",
        "security": [
          {
            "bearerAuth": [
              "mandato:write"
            ]
          },
          {
            "bearerAuth": [
              "mandato:attest"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MandatoCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The mandato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Mandato"
                },
                "examples": {
                  "default": {
                    "summary": "Empty body: mints the signing link for the representante legal.",
                    "value": {
                      "object": "mandato",
                      "empresa_id": "emp_01K2R6ZC4P8QW1VN7T3MHDY9EK",
                      "status": "pending",
                      "terms_version": "1.0",
                      "terms_hash": "sha256:6447ef0de8961…",
                      "terms_current": true,
                      "representante_legal": null,
                      "signing_url": "https://app.facturia.cl/firmar/mandato/tok_9F3kQ2wZ8pR1x7bN0m2Y4hLQ",
                      "signing_expires_at": "2026-09-09T15:04:00Z",
                      "signed_at": null,
                      "acceptance_method": null,
                      "revoked_at": null,
                      "pending_action": {
                        "object": "pending_action",
                        "act": "mandato",
                        "step": "mandato",
                        "empresa_id": "emp_01K2R6ZC4P8QW1VN7T3MHDY9EK",
                        "action_url": "https://app.facturia.cl/firmar/mandato/tok_9F3kQ2wZ8pR1x7bN0m2Y4hLQ",
                        "link": {
                          "mint": {
                            "method": "POST",
                            "url": "https://api.facturia.cl/v1/empresas/emp_01K2R6ZC4P8QW1VN7T3MHDY9EK/mandato",
                            "body": {}
                          },
                          "ttl_seconds": 604800,
                          "reusable": false
                        },
                        "title": "Firmar el mandato que nos autoriza a actuar ante el SII",
                        "summary": "Es el contrato que nos permite gestionar tus documentos tributarios ante el SII por cuenta tuya. Lo lee y lo acepta el representante legal, en una página nuestra, y se puede revocar en cualquier momento.",
                        "sii_url": null,
                        "performed_by": {
                          "role": "representante_legal",
                          "label": "El representante legal de la empresa.",
                          "why_not_delegable": "Es el acto por el que se otorga el mandato: quien lo firma tiene que ser quien puede obligar a la empresa. Sin él no podemos actuar ante el SII por ti en absoluto.",
                          "legal_basis": [
                            "Mandato Facturia, cláusula 2.5 «Suscripción con firma electrónica simple»",
                            "Código Civil, artículo 2116 y siguientes"
                          ]
                        },
                        "prerequisites": [
                          {
                            "key": "identidad_rep_legal",
                            "label": "Nombre, RUT y correo del representante legal que va a aceptar."
                          },
                          {
                            "key": "rut_empresa",
                            "label": "El RUT de la empresa: 77928532-4."
                          }
                        ],
                        "instructions": [
                          {
                            "order": 1,
                            "text": "Abre el enlace de firma que te entregamos. Es de un solo uso y caduca; si lo perdiste, pide otro y el anterior queda revocado."
                          },
                          {
                            "order": 2,
                            "text": "Lee el contrato completo. Presta atención a la cláusula 4.6: enumera los actos que la ley reserva a tu representante legal y que nosotros no ejecutaremos nunca por ti."
                          },
                          {
                            "order": 3,
                            "text": "Acepta. Guardamos la versión aceptada, su huella SHA-256, la fecha, la IP y quién aceptó, para que puedas verificar después que el texto no cambió."
                          }
                        ],
                        "consequences": [
                          "La aceptación queda registrada con firma electrónica simple. Si prefieres firma electrónica avanzada, pídenoslo: está previsto en el contrato.",
                          "Puedes revocar el mandato cuando quieras y con efecto inmediato. La revocación no deshace nada de lo ya hecho."
                        ],
                        "verification": {
                          "method": "own_record",
                          "description": "La aceptación se registra en el mismo acto en nuestros sistemas. No hay nada que consultarle al SII.",
                          "recheck_url": "POST /v1/empresas/emp_01K2R6ZC4P8QW1VN7T3MHDY9EK/onboarding/steps/mandato/recheck",
                          "sii_latency": "Inmediato: no interviene el SII."
                        },
                        "estimated_minutes": 10,
                        "next": "Con el mandato firmado te pedimos el certificado digital y arrancamos el proceso.",
                        "evidence": "captured",
                        "caveat": null,
                        "agent_actions": [],
                        "copy": null
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "delete": {
        "tags": [
          "Empresas"
        ],
        "operationId": "revokeMandato",
        "summary": "Revoke the mandato",
        "description": "Cláusula 7.1: at any time, without cause, without penalty, and without ending the\ncontract. The empresa moves to `suspended` immediately, everything already queued is\ncancelled in the same transaction, and the `mandato` step returns to `action_required`.\n\n**This is an atestación, not a write (ADR-0012 §4, ticket #465).** It is the act with\nthe largest effect in the contract, so `empresas:write` is NOT enough: send it with\n`mandato:attest`, which is granted explicitly, is never implied by `empresas:write` and\nis never in the MCP vocabulary. A dashboard session may also call it — a session is the\naccount holder in person. Anything else gets `403 insufficient_scope`, whose\n`error.details` carries `required_scope` and the `{method, url}` that mints the hosted\nrevocation page for the representante legal\n(`POST /empresas/{empresa_id}/mandato/revocation_link`).\n\nIt does NOT destroy the certificate — that is `DELETE .../certificate`, a separate\nrevocation with its own deadline. And it does not undo what preceded it: the empresa's\nposition is remembered, so a fresh acceptance returns it to where it was rather than\nrestarting onboarding.\n\nThe empresa stays READABLE while suspended (ticket #515): `GET /empresas/{id}`,\n`GET .../onboarding` and `GET .../mandato` all answer, `onboarding.blocked_on` names\n`mandato`, and `onboarding.pending_action` is the `mandato_revocado` handoff whose\n`link.mint` re-signs. `active` is untouched — a right the contract calls reversible is\nnever implemented as a disappearance.\n",
        "security": [
          {
            "bearerAuth": [
              "mandato:attest"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked. The empresa moves to `suspended`, and stays readable."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/mandato/revocation_link": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        }
      ],
      "post": {
        "tags": [
          "Empresas"
        ],
        "operationId": "createMandatoRevocationLink",
        "summary": "Mint the hosted page where the representante legal revokes",
        "description": "Cláusula 7.1 belongs to the person who signed, and this is how you put them in front of\nit (ticket #465). The acceptance already hands them this address once, and the receipt\ne-mail carries it; this endpoint is for every later moment — the e-mail was lost, or\nnever went out, or the customer is asking their integrator to help them stop.\n\nHolding the link is not holding the power: the page demands the representante legal's\nRUT **as recorded on the mandato they signed**, which this response deliberately does\nnot echo. Minting revokes any previous revocation link, which is what single-use means\nwhen a link is reissued.\n\nThe URL is returned EXACTLY ONCE — only a SHA-256 of the token is stored. It does not\nexpire: the right it addresses does not either.\n\n`409 mandato_not_signed` when nothing is in force, because an address for a right that\ncannot be exercised is worse than a refusal that says so.\n",
        "security": [
          {
            "bearerAuth": [
              "mandato:write"
            ]
          }
        ],
        "responses": {
          "201": {
            "description": "The link. Returned once.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MandatoRevocationLink"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/resolucion/verify": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        }
      ],
      "post": {
        "tags": [
          "Empresas"
        ],
        "operationId": "verifyResolucionClaim",
        "summary": "Verify a declared resolución with one production timbraje (Facturia operators only)",
        "description": "**OPERATOR ONLY — NOT CALLABLE WITH AN API KEY** (sii-api#581 D1, founder decision\n2026-09-23). Documented so an integrator knows what moves `resolucion.verification_status`\nand who moves it.\n\nA resolución stated on `POST`/`PATCH /v1/empresas` is a claim. It is verified by asking\nthe PRODUCTION SII for ONE folio range of one declared factura tipo: the SII refuses a\ntimbraje for a RUT it has not authorized for that tipo, so a granted range is the proof.\nThat request is irreversible (a registered range must be used or annulled), so it is made\nonly when a Facturia operator confirms it for this empresa. Nothing is requested\nimplicitly, nothing during `POST /v1/empresas`, and nothing against the certification\nenvironment.\n\nWithout `confirm: true` the call refuses with `400 confirmation_required` and names the\nexact request in the message and in `error.details.request`. With it, the confirmation\n(who, when, why) is recorded and ONE `folio_request` job is queued; the SII's answer then\nmoves `resolucion.verification_status` from `pending_verification` to `verified` or\n`refused`. An attempt that gets no answer about the RUT (the SII's edge block, an unusable\ncertificate) stays `pending_verification`.\n\n`409 resolucion_verification_not_allowed` names the blocking fact in\n`error.details.reason`: `sandbox`, `sfg_channel`, `no_claim`, `already_verified`,\n`refused`, `in_flight`, `range_registered`, `not_ready` (the empresa is not at\n`certificate_verified`) or `no_factura_tipo`.\n",
        "security": [
          {
            "operatorSecret": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResolucionVerifyRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Confirmed. The one production request is queued; the SII's answer lands on `resolucion`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResolucionVerification"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/counterparties": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        }
      ],
      "get": {
        "tags": [
          "Empresas"
        ],
        "operationId": "listCounterparties",
        "summary": "Counterparties you have invoiced, with the address and contacts on file for each",
        "description": "A DERIVED list, not a stored one: it reads the receivers of the documents this empresa\nhas already emitted (tipos 33, 34, 56 and 61) and joins whatever contacts and address\nbook entries exist, so the screen opens as a checklist rather than a blank form. A\ncounterparty that has an address or a contact but no document yet is included.\n\nThe address fields come from the address book only. Where none is on file they are null\nand so is `source`, which on this list means \"nothing recorded\" rather than \"nothing\nknown\" — the single-counterparty read is the one that falls back to what earlier\ndocuments said.\n\n`has_more` is always `false`. The window bounds the SCAN, not a page of results, and\npromising a cursor this shape cannot honour would be worse than saying so.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The counterparties, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Counterparty"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/contribuyentes/{rut}": {
      "parameters": [
        {
          "name": "rut",
          "in": "path",
          "required": true,
          "description": "Any persona jurídica's RUT, canonical (`76000002-7`) or with dots, or without the hyphen.",
          "schema": {
            "$ref": "#/components/schemas/Rut"
          }
        }
      ],
      "get": {
        "tags": [
          "Empresas"
        ],
        "operationId": "getContribuyente",
        "summary": "What the SII publishes about a company, by RUT, and a giro that fits a factura",
        "description": "The razón social and economic activities the SII publishes for a persona jurídica, and a\nsuggested `giro` that fits the receptor of a factura (at most 40 characters, ISO-8859-1).\nFor prefilling a customer before the first invoice to them.\n\nThe source is the SII's public nómina of personas jurídicas\n(https://www.sii.cl/sobre_el_sii/nominapersonasjuridicas.html), which the SII republishes\nevery few months and does not guarantee to be complete or current. `source.published_at`\nsays which edition answered. Personas naturales are not in it: their RUTs answer 404, like\na RUT that does not exist.\n\nA SUGGESTION, not a statement: nothing here is written into a document on its own. Show it\nto the person, and once confirmed, put it on file with\n`PATCH /v1/empresas/{id}/counterparties/{rut}` — that is what the next emission reads.\n\n`suggested_giro.basis` is `curated` when the text comes from Facturia's short wording for\na common activity code (the official descriptions are usually longer than 40 characters,\nand a cut one would change what the factura says), `sii_description` when the official\ndescription fits as it stands. Null when neither applies: ask the person.\n\nNot empresa-scoped: the data is public and about a third party, so no `Facturia-Empresa`\nis needed. Limited to 120 requests a minute per account.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The contribuyente.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contribuyente"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The nómina does not list this RUT (a persona natural, or no such company).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "description": "The nómina has not been loaded yet (`contribuyentes_unavailable`). Retry after `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/counterparties/{counterparty_rut}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        },
        {
          "$ref": "#/components/parameters/CounterpartyRut"
        }
      ],
      "get": {
        "tags": [
          "Empresas"
        ],
        "operationId": "getCounterparty",
        "summary": "What you know about one counterparty, and whether you stated it or we inferred it",
        "description": "Your address book entry for this RUT — the razón social, giro and domicilio we will put\non the next document to it.\n\nTwo sources, in the order the emission resolver uses them. If you have put an address on\nfile, that is the answer and `source` is `api`. If you have not, but you have already\nemitted a document to this RUT, the answer is read off the newest one and `source` says\nso — that is inference, not a statement, and it is the difference worth knowing before\nyou spend a folio. If neither exists, this is a 404.\n\nThe counterparty has no id: there is exactly one identity per RUT, so the RUT is the key.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The counterparty.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CounterpartyIdentity"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Nothing is on file for this RUT and no document has ever been emitted to it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "patch": {
        "tags": [
          "Empresas"
        ],
        "operationId": "updateCounterparty",
        "summary": "Put a counterparty's razón social, giro and address on file",
        "description": "The SII will not file a factura whose receptor has no giro, dirección and comuna: it\naccepts the ENVELOPE and then refuses the DOCUMENT, and the folio is spent either way.\nRecording the address here answers that once instead of on every emission — and it is\nthe only source that can answer for a counterparty you have never invoiced, which is\nexactly when the question bites.\n\nAn upsert: the first call creates the entry, later ones edit it. There is no POST and no\nDELETE, because the entry has no lifecycle of its own.\n\nPartial. A field you do not send keeps its value; a field sent as `null` is cleared.\nEvery value is held to the same SII schema limit a value sent on a document is held to —\n`giro` 40 characters, `address.street` 70, `address.comuna` and `address.city` 20,\n`legal_name` 100 — and to the same ISO-8859-1 rule. Over the limit is refused, never\ntruncated: a silently shortened name of trade would change what every document to this\ncounterparty says about who they are.\n\nNo earlier document is required. Recording an address for a RUT you have not invoiced\nyet is the point.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CounterpartyUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The stored counterparty.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CounterpartyIdentity"
                }
              }
            }
          },
          "400": {
            "description": "A malformed RUT, a body naming no field at all, a value longer than the SII element\nit becomes (`field_too_long`) or one carrying a character ISO-8859-1 cannot represent\n(`unencodable_character`).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/counterparties/{counterparty_rut}/contacts": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        },
        {
          "$ref": "#/components/parameters/CounterpartyRut"
        }
      ],
      "get": {
        "tags": [
          "Empresas"
        ],
        "operationId": "listCounterpartyContacts",
        "summary": "The contacts on file for one counterparty",
        "description": "The primary contact comes first — it is the only one that is notified.",
        "security": [
          {
            "bearerAuth": [
              "empresas:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The contacts.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/CounterpartyContact"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "post": {
        "tags": [
          "Empresas"
        ],
        "operationId": "createCounterpartyContact",
        "summary": "Put a contacto responsable on file",
        "description": "A contact with no attestation is a legitimate state: it is stored, it is shown, and it\nis never messaged. Sending `opt_in: true` without `opt_in_method` is refused, because\nconsent recorded without recording how it was obtained is not a record.\n\nThe first contact for a counterparty becomes the primary unless you say otherwise.\n\nA number that has opted out cannot be re-added under a new id: that is `422\ncontact_opted_out`, and it is what stops delete-and-recreate from undoing a STOP.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CounterpartyContactCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The contact.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CounterpartyContact"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/counterparties/{counterparty_rut}/contacts/{contact_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        },
        {
          "$ref": "#/components/parameters/CounterpartyRut"
        },
        {
          "name": "contact_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The `cnt_…` id."
        }
      ],
      "patch": {
        "tags": [
          "Empresas"
        ],
        "operationId": "updateCounterpartyContact",
        "summary": "Edit a contact, promote it, or record an opt-out",
        "description": "`primary: true` demotes whoever held it, in the same call — exactly one contact per\ncounterparty may be primary and the database is what enforces it.\n\n`opted_out: true` records that the person asked to stop by some other channel.\n`opted_out: false` does not exist: an opted-out contact is frozen and cannot be edited,\nre-attested or re-enabled. Someone who asked to be left alone is not un-asked by a\ncolleague fixing a typo in their name.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CounterpartyContactUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated contact.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CounterpartyContact"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "delete": {
        "tags": [
          "Empresas"
        ],
        "operationId": "deleteCounterpartyContact",
        "summary": "Remove a contact from the address book",
        "description": "204 whether or not it was there. The exception is an opted-out contact, which is kept:\nits row is precisely what makes re-adding the same number refusable.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Removed."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/onboarding": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        }
      ],
      "get": {
        "tags": [
          "Empresas"
        ],
        "operationId": "getOnboarding",
        "summary": "Onboarding lifecycle, steps and blocking human actions",
        "description": "Everything a wizard needs to render progress and tell the user what to do next.",
        "security": [
          {
            "bearerAuth": [
              "empresas:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Onboarding state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Onboarding"
                },
                "examples": {
                  "default": {
                    "summary": "An SFG empresa, fully emitting — every step completed.",
                    "value": {
                      "object": "onboarding",
                      "empresa_id": "emp_01K2R9F3T8QW2X",
                      "mode": "live",
                      "status": "emitting",
                      "progress": 1,
                      "blocked_on": null,
                      "pending_action": null,
                      "estimated_ready_at": null,
                      "requirements": [],
                      "steps": [
                        {
                          "key": "mandato",
                          "label": "Mandato firmado",
                          "status": "completed",
                          "actor": "customer",
                          "help": "Nos autoriza a actuar ante el SII por tu empresa. Lo firma el representante legal y se puede revocar cuando quieras.",
                          "action_url": null,
                          "pending_action": null,
                          "completed_at": "2026-08-25T12:00:00Z",
                          "evidence_source": "own_record"
                        },
                        {
                          "key": "credential",
                          "label": "Certificado o delegación configurada",
                          "status": "completed",
                          "actor": "customer",
                          "help": "Para operar tu cuenta del Sistema de Facturación Gratuito necesitamos, o tu certificado digital en custodia, o que registres el RUT de Facturia como usuario autorizado en tu Portal MIPYME.",
                          "action_url": null,
                          "pending_action": null,
                          "completed_at": "2026-08-25T12:00:00Z",
                          "evidence_source": "sii_probe"
                        },
                        {
                          "key": "clave_centralizado",
                          "label": "Clave del Sistema de Facturación Gratuito",
                          "status": "completed",
                          "actor": "customer",
                          "help": "Es la clave con la que entras al Sistema de Facturación Gratuito en sii.cl. Se guarda cifrada en la bóveda.",
                          "action_url": null,
                          "pending_action": null,
                          "completed_at": "2026-08-25T12:00:00Z",
                          "evidence_source": "own_record"
                        },
                        {
                          "key": "usuario_autorizado",
                          "label": "Usuario autorizado verificado",
                          "status": "completed",
                          "actor": "customer",
                          "help": "Comprobamos que el certificado de Facturia puede actuar por tu empresa en el Portal MIPYME.",
                          "action_url": null,
                          "pending_action": null,
                          "completed_at": "2026-08-25T12:00:00Z",
                          "evidence_source": "sii_probe"
                        },
                        {
                          "key": "emisor_verificado",
                          "label": "Emisor autorizado verificado",
                          "status": "completed",
                          "actor": "facturia",
                          "help": "Confirmamos que tu empresa ya está inscrita en el Sistema de Facturación Gratuito y autorizada a emitir.",
                          "action_url": null,
                          "pending_action": null,
                          "completed_at": "2026-08-25T12:00:00Z",
                          "evidence_source": "sii_probe"
                        },
                        {
                          "key": "puesta_en_vivo",
                          "label": "Puesta en vivo",
                          "status": "completed",
                          "actor": "facturia",
                          "help": "Nos toca a nosotros. Antes de que tu empresa emita su primer documento real hacemos una emisión de prueba acompañada.",
                          "action_url": null,
                          "pending_action": null,
                          "completed_at": "2026-08-25T12:00:00Z",
                          "evidence_source": "own_record"
                        },
                        {
                          "key": "emitting",
                          "label": "Emitiendo",
                          "status": "completed",
                          "actor": "facturia",
                          "help": "Todo listo: emitimos y recibimos tus documentos a través de tu cuenta del Sistema de Facturación Gratuito.",
                          "action_url": null,
                          "pending_action": null,
                          "completed_at": "2026-08-25T12:00:00Z",
                          "evidence_source": "own_record"
                        }
                      ],
                      "certifications": [],
                      "iva_shape": null,
                      "updated_at": "2026-08-25T12:00:00Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/onboarding/links": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        }
      ],
      "post": {
        "tags": [
          "Empresas"
        ],
        "operationId": "createOnboardingLink",
        "summary": "Mint a link to a hosted representante-legal page",
        "description": "The person who has to act is often not the person holding the account: the account\nbelongs to the accountant, the certificate belongs to a socio, and the SII steps that\nneed a person are the socio's. So the hosted pages are reached by a link, not by a\nlogin.\n\n`step` is any step of THIS empresa's checklist that needs a person - `actor: customer`\n(the step they perform) OR `stall_actor: customer` (a step of OURS that cannot finish\nuntil they decide). Which ones exist depends on its `certification_track`:\n`certificate`, `sii_postulacion`, `cumplimiento` and `habilitacion` on the mercado\ncatalog; `credential`, `clave_centralizado` and `usuario_autorizado` on the SFG one. A\nstep from the other catalog is refused with a `400` naming the ones this empresa does\nhave. The mandato's link is single-use and comes from\n`POST /empresas/{empresa_id}/mandato` with an empty body instead.\n\nThe `url` is returned exactly once - only its SHA-256 is stored. The link is reusable\nand lives thirty days, and re-minting revokes the previous one.\n\nThe response also carries the step's `pending_action`, with `action_url` set to the URL\njust minted: the mint is the one moment both halves exist at once, so an agent gets the\nlive address and the instructions to relay with it in a single call.\n",
        "security": [
          {
            "bearerAuth": [
              "onboarding:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OnboardingLinkCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The link. `url` carries the token and is never shown again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OnboardingLink"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/onboarding/links/{step}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        },
        {
          "name": "step",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The step whose link is being revoked. Any step either catalog can mint a link for -\nthe wider set on purpose, so a link minted under a checklist the empresa has since\nleft can still be taken back.\n"
        }
      ],
      "delete": {
        "tags": [
          "Empresas"
        ],
        "operationId": "revokeOnboardingLink",
        "summary": "Revoke a hosted representante-legal link",
        "description": "204 whether or not one existed. A revoked link answers exactly like one that never\nexisted, so a token cannot be used to learn whether we ever asked a given company to\nsign something.\n",
        "security": [
          {
            "bearerAuth": [
              "onboarding:write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/onboarding/go_live": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        }
      ],
      "post": {
        "tags": [
          "Empresas"
        ],
        "operationId": "authorizeGoLive",
        "summary": "Ask the titular to approve the go-live, or state the decision on their behalf",
        "description": "The Declaracion de Cumplimiento is a decision about a month, not a progression. On\ndeclaring, the empresa \"queda operando automaticamente en el sistema de facturacion\nelectronica\" and owes the Informacion Electronica de Compras y Ventas FROM THE MONTH OF\nTHE DECLARACION, whether or not it emits anything. The SII's own page says the step may\nbe postponed.\n\nSo the month is the customer's to choose and a custody mandate does not carry it.\n\n**TWO FORMS, and the body is which one.**\n\n- **No body** mints an *enlace de aprobacion* for the titular and answers `201` with\n  `approval_url` and `expires_at`. The human opens the page, sees the month and the\n  IECV consequence in their own language, and decides. Single use, seven days, and the\n  URL is returned exactly once - only its SHA-256 is stored. Scope `onboarding:write`.\n- **`{\"authorized\": true|false}`** states the decision on the titular's behalf: an\n  *atestacion*, for an integrator that collected it in its own flow. Answers `200` with\n  the authorization. Scope `onboarding:attest`.\n\n`empresas:write` implies `onboarding:write` — the no-body form — but NOT\n`onboarding:attest` (corrected 2026-09-03): an atestación is a human granting that power\non purpose, every time, never a side effect of a wider grant a key already held. A key\nthat used to send `{\"authorized\": true|false}` under `empresas:write` alone now gets\n`403 insufficient_scope` naming `onboarding:attest`.\n\n**This changed in 2026-09.** An empty body used to DEFAULT to `authorized: true`, so a\ncaller who sent no body silently authorized a tax consequence. An omitted field is not a\ndecision; if you were relying on that, send `{\"authorized\": true}` explicitly.\n\n`{\"authorized\": false}` withdraws while nothing has been filed. Once the declaracion is\nfiled there is nothing left to withdraw and the call is refused with\n`409 cumplimiento_already_filed`.\n\n**Refusals arrive together.** The status and code are the first blocker's own - `422\nmandato_revoked`, for instance - and `error.details.blockers[]` lists every one, so you\ndo not learn them one round trip at a time.\n\n**THE AUTHORIZATION IS FREE** (2026-09-04, #530). It used to be gated on the\nsubscription, and only on this door: the hosted `cumplimiento` page for the\nrepresentante legal answered `200` for the same unsubscribed empresa, so the rule lived\nin one channel and an agent that sent the human to the other one walked around it. It\nis a DECISION, not an emission — the titular saying «start my obligation this month» —\nand the gate stands where the value is: every act that spends a folio or leaves the\nbuilding is still gated, so an authorized empresa that has not paid emits nothing. This\noperation no longer answers `402`.\n\n**There has to BE a decision (2026-09, #451).** Minting an approval link for an empresa\nthat has not reached the Declaracion de Cumplimiento handed a human a page with nothing\non it. Two refusals come first, before the blockers and before the mint:\n\n- `409 go_live_not_ready` - the mercado lane has not reached the decision.\n  `error.details.blocked_on` names the step holding it up and `error.details.next` is\n  the `{method, url}` that reports when it moves. Retriable: it WILL be ready.\n- `409 go_live_not_applicable` - this empresa is not certifying for mercado, so it has\n  no Declaracion de Cumplimiento at all. What gates an SFG empresa's emission is\n  `puesta_en_vivo`, which is ours. Not retriable.\n\nNeither applies to `{\"authorized\": false}`: a decision that exists can always be\nwithdrawn, whatever the lane has done since.\n",
        "security": [
          {
            "bearerAuth": [
              "onboarding:write"
            ]
          },
          {
            "bearerAuth": [
              "onboarding:attest"
            ]
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GoLiveRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The authorization, as it now stands. Answered for the body form.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GoLiveAuthorization"
                }
              }
            }
          },
          "201": {
            "description": "The approval link, answered for an empty body. `approval_url` is shown once.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GoLiveApproval"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/onboarding/steps/{step}/recheck": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        },
        {
          "name": "step",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "An onboarding step key from this empresa's own checklist."
        }
      ],
      "post": {
        "tags": [
          "Empresas"
        ],
        "operationId": "recheckOnboardingStep",
        "summary": "Tell us a human step was performed, and we go and look",
        "description": "The resume half of the `pending_action` handoff. A person has performed one of the acts\nChilean law reserves to them (see `pending_action.performed_by`), and this asks us to\nfind out whether the SII agrees.\n\n**This endpoint accepts a claim and produces no fact.** It takes no request body, writes\nno step status and moves no empresa. What it does is enqueue the producer that READS the\nSII, plus the tick that folds whatever that producer finds, and return the step's status\nas it stood the moment you called — read before the enqueue and returned unchanged. If\nyou post a recheck on a step that is `action_required` and read `action_required` back,\nthat is the endpoint working: the step advances when we see the fact, not when you say\nso. `verified` is on the response and it is always `false`.\n\n`evidence_source` says how (or whether) the step can be confirmed at all. `sii_probe`\nmeans we go and read the SII. `own_record` means the act lands in our own tables and we\nwill see it. `no_producer` means this build genuinely cannot observe that step yet — the\nboleta `habilitacion` is the one — and `contact` names the door out:\n`POST .../onboarding/steps/{step}/attest` (#395), which is a DIFFERENT endpoint that\nwrites a fact rather than merely recording a claim. Do not tell a user this call alone is\nwatching a `no_producer` step; nothing is.\n\n`queued` and `already_queued` split the jobs this call created from the ones that were\nalready scheduled for the same five-minute window. Both mean \"we are about to look\".\n\nFor a sandbox empresa nothing is enqueued and `evidence_source` is `sandbox`: a test\nempresa's onboarding never opens a session against the SII, so there is nothing to\nre-read. Move its steps with the test helpers instead.\n",
        "security": [
          {
            "bearerAuth": [
              "onboarding:write"
            ]
          }
        ],
        "responses": {
          "202": {
            "description": "The claim was accepted for verification. Nothing has been verified yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OnboardingRecheck"
                },
                "examples": {
                  "default": {
                    "summary": "The claim was accepted; the step had not advanced yet.",
                    "value": {
                      "object": "onboarding_recheck",
                      "empresa_id": "emp_01K2R9F3T8QW2X",
                      "step": "usuario_autorizado",
                      "status": "action_required",
                      "accepted": true,
                      "verified": false,
                      "evidence_source": "sii_probe",
                      "contact": null,
                      "queued": [
                        "onboarding_step_probe"
                      ],
                      "already_queued": [],
                      "message": "Recibimos tu aviso. Vamos a revisar el Portal MIPYME y te avisamos apenas veamos el cambio.",
                      "pending_action": null,
                      "onboarding_url": "/v1/empresas/emp_01K2R9F3T8QW2X/onboarding",
                      "requested_at": "2026-09-02T10:00:00Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/onboarding/steps/{step}/attest": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        },
        {
          "name": "step",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "An onboarding step key from this empresa's own checklist."
        }
      ],
      "post": {
        "tags": [
          "Empresas"
        ],
        "operationId": "attestOnboardingStep",
        "summary": "Record that a human performed a step this build cannot probe (#395)",
        "description": "The endpoint `recheck`'s own `contact` field points at when `evidence_source:\n'no_producer'`. Unlike `recheck` — which accepts a claim and produces no fact —\n**this endpoint writes one**: a `sii_onboarding_evidence` row the next onboarding tick\nreads as the fact that lets the step complete.\n\nRefused with `422 invalid_field` on any step whose `evidence_source` is not\n`no_producer` — attesting a step the SII probe or our own records already confirm would\nbe a second, competing source of truth for a fact we can already verify. `habilitacion`\n(boleta habilitación at `certBolElectDteInternet`, an uncaptured SII SPA) is the only\nstep this map names `no_producer` today.\n\nRequires `onboarding:attest` **itself**: never in the bootstrap default set, never\nimplied by `empresas:write` alone (ADR-0012 §3-4). `attested_by` is required — who told\nyou this happened.\n",
        "security": [
          {
            "bearerAuth": [
              "onboarding:attest"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "attested_by"
                ],
                "properties": {
                  "attested_by": {
                    "type": "string",
                    "description": "A name, or the representante legal's RUT. Who told you this happened."
                  },
                  "note": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The attestation was recorded. The step folds on the next tick.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OnboardingAttestation"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/onboarding/steps/{step}/approve": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        },
        {
          "name": "step",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "An onboarding step key from this empresa's own checklist."
        }
      ],
      "post": {
        "tags": [
          "Empresas"
        ],
        "operationId": "approveOnboardingStep",
        "summary": "Record the titular's authorization for a step Facturia then performs (#452, #453)",
        "description": "The sibling of `attest`, for the opposite situation. `attest` says «a person did\nsomething we cannot see»; this says «a person authorized something WE are about to do».\n\nTwo acts in this pipeline are ours to perform once somebody chooses the moment. The\npostulacion is presented with the representante legal's own custodied certificate under\nmandato 4.2 a), and it costs an empresa on the Sistema de Facturacion Gratuito its\n\"calidad de usuario activo\" the instant the SII authorizes it. The boleta habilitacion\nopens a 24-hour SII clock. Neither is a decision a custody mandate carries, so each\nwaits for a row in `sii_onboarding_step_approvals`.\n\n**`act` must be the one the step is currently asking for.** Read\n`steps[].pending_action.approval` on `GET .../onboarding`: it names the act, whose\ndecision it is, the consequences to show first, and both button labels. A step asking\nfor nothing answers `409 onboarding_approval_not_offered`; a mismatched `act` answers\n`400 invalid_field`. One step carries different decisions at different points of its\nlife, and an approval recorded against the wrong one is consent that was never given.\n\n**A rejection is recorded, not ignored.** `decision: \"reject\"` writes a row exactly as\nan approval does, so the product can say \"you left it for later\" rather than \"nobody\nanswered\". Executors read the LATEST row for the act and branch on `decision`.\n\nRequires `onboarding:attest` itself — never in the bootstrap default set, never implied\nby `empresas:write` (ADR-0012 §3-4). The canonical form of an approval is the hosted\npage (`POST /onboarding/steps/{step}/approve` with the link token); this is the\nintegrator door for a decision collected in their own flow.\n",
        "security": [
          {
            "bearerAuth": [
              "onboarding:attest"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "act",
                  "decision"
                ],
                "properties": {
                  "act": {
                    "type": "string",
                    "description": "The act being decided, exactly as `pending_action.approval.act` names it."
                  },
                  "decision": {
                    "type": "string",
                    "enum": [
                      "approve",
                      "reject"
                    ]
                  },
                  "note": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The decision was recorded. On `approve`, the fold tick is enqueued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OnboardingStepApproval"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/onboarding/certification": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        }
      ],
      "get": {
        "tags": [
          "Empresas"
        ],
        "operationId": "getCertification",
        "summary": "The set de pruebas - what was uploaded, what is missing, what the SII said",
        "description": "One read, not four. A certification is a bounded thing - one set file, one input\ndocument, a handful of envios across at most three rounds - and splitting it would\nmake the obvious screen four round trips whose answers can disagree.\n\nTWO FIELDS THAT LOOK ALIKE AND ARE OPPOSITE. `envios[].estado` is the ENVIO estado,\nwhere `SOK` means \"envio recibido\" - arrived, not judged. `envios[].verdict` is the\nSET verdict, where `SOK` means the set was APPROVED. Same three letters, opposite\nconsequences, which is why they are two fields and never merged.\n\n`envios[].dry_run: true` means the envelope was built and NOT sent. `submittable` is\n`null` until a build has happened at all, which is not the same as `false`.\n\n`problems[].message` may be `null`: the sentences live in the build artifact beside\nthe record, and when it cannot be read the response degrades to codes rather than\nfailing. The code is always there.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The certification, as it now stands.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Certification"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/onboarding/certification/set_file": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        }
      ],
      "post": {
        "tags": [
          "Empresas"
        ],
        "operationId": "uploadCertificationSetFile",
        "summary": "Upload the set de pruebas the SII issued for this empresa",
        "description": "The SII issues the set `.txt` per taxpayer when it accepts the postulacion. Nothing\ndownloads it for you; this is how the bytes get in.\n\nMULTIPART, NOT BASE64 IN JSON. The file is ISO-8859-1 and its accents are part of the\ndocument - `Cajon` and `Panuelo` are two of the items the SII reported as reparos when\ntheirs were lost. Multipart hands us the bytes; a JSON string would already have been\nthrough somebody's idea of a text encoding by the time we saw it. The bytes are stored\nverbatim and never re-encoded. Limit: 1 MB.\n\nBytes that are not a set de pruebas are refused with `422 set_file_unrecognized` and\nNOTHING is stored - a portal login page parsed as an empty set would report the\nempresa as having no casos, which is a different thing from having no set file.\n\nA set that IS recognised is stored even when parts of it were not understood; those\nappear in `problems[]`.\n\nThis endpoint does not run a round, does not open a socket to the SII and does not\nspend a folio. The round is the engine's, on its own clock.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The `.txt` as the SII issued it. ISO-8859-1, at most 1 MB."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The set as we read it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificationSetFile"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/onboarding/certification/inputs": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        }
      ],
      "post": {
        "tags": [
          "Empresas"
        ],
        "operationId": "setCertificationInputs",
        "summary": "Supply the facts the set de pruebas does not carry",
        "description": "The set states several things by implication and never spells them out: who issued\neach purchase in the libro de compras, what the CodRef-2 correction says, which\nfactura invoiced a guia the set marks \"facturada\", the factor de proporcionalidad,\nthe SII-issued folio de notificacion, and - for a boleta round - the abbreviated\nunidades and ten everyday boletas. Each one is a deliberate refusal in the engine:\nthe round reports what it needs rather than inventing a value and spending folios on\na document the SII will reject.\n\nTHE MERGE IS PER TOP-LEVEL SLOT: absent keeps, `null` clears, present replaces WHOLE.\nRestating `proveedores` restates all of them - which is what a form submits and the\nonly way to REMOVE one. A deep merge would leave the row you deleted in the next\nlibro.\n\nNOTHING IS WRITTEN WHEN ANYTHING IS INVALID, not even the valid half: a partial write\nleaves the empresa in a state nobody asked for and a round that refuses for a reason\nyou believe you already fixed. `400 certification_input_invalid` carries EVERY problem\nin `error.details.problems`, not the first. It may also refuse over a value you did\nnot send, when the stored document your patch merges into is itself invalid.\n\nONE SLOT IS PREFILLED: `libro_guias.folio_notificacion` defaults to 3, the number\ncontinuing the 1 and 2 the libro de ventas and the libro de compras carry.\nLibroGuia_v10.xsd makes the element mandatory where the IECV's is optional, so it\ncannot simply be omitted, and a round that goes out on the default says so in a\nnotice. `libro_boleta_folio_notificacion` has no default and will not get one: it is\nan SII-issued number answering a specific notificacion and nothing can derive it.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CertificationInputs"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The inputs as they now stand on the empresa.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "certification_inputs"
                    },
                    "empresa_id": {
                      "type": "string"
                    },
                    "inputs": {
                      "$ref": "#/components/schemas/CertificationInputs"
                    },
                    "updated": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "The slots this call touched."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/onboarding/evidence": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        },
        {
          "$ref": "#/components/parameters/Limit"
        },
        {
          "$ref": "#/components/parameters/Cursor"
        },
        {
          "name": "step",
          "in": "query",
          "required": false,
          "schema": {
            "type": "string"
          },
          "description": "Only artifacts of this onboarding step."
        },
        {
          "name": "kind",
          "in": "query",
          "required": false,
          "schema": {
            "type": "string"
          },
          "description": "Only artifacts of this kind."
        }
      ],
      "get": {
        "tags": [
          "Empresas"
        ],
        "operationId": "listOnboardingEvidence",
        "summary": "The rendicion de cuentas - what we did on the empresa's behalf",
        "description": "Clausula 4.4 of the mandato obliges us to render account of what we did on the\ncustomer's behalf. Every act the onboarding pipeline performs is kept as an artifact,\nand this reads them back, newest first.\n\n`retention_until` is on every row and is six years from the act - the Codigo\nTributario's prescripcion extraordinaria plus its conservation duty, which is also the\nretention purpose under Ley 21.719. On revocation or traspaso the record freezes\nread-only and is offered as an export; it is never purged early.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The artifacts.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/OnboardingEvidence"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/onboarding/evidence/export": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        },
        {
          "name": "step",
          "in": "query",
          "required": false,
          "schema": {
            "type": "string"
          },
          "description": "Only artifacts of this onboarding step."
        },
        {
          "name": "kind",
          "in": "query",
          "required": false,
          "schema": {
            "type": "string"
          },
          "description": "Only artifacts of this kind."
        }
      ],
      "get": {
        "tags": [
          "Empresas"
        ],
        "operationId": "exportOnboardingEvidence",
        "summary": "The rendicion de cuentas as one CSV file",
        "description": "The same rows the list endpoint serves, complete in one response, as a file you can hand\nto a customer, an accountant or a lawyer. The retention ruling says that on revocation or\ntraspaso we offer the customer a full export as the formal clausula 4.4 rendicion; this is\nthat export, and it is available at any time, not only on the way out.\n\nRFC 4180, comma-separated, UTF-8 with NO byte-order mark, CRLF line endings. The columns\nare the JSON object's own fields in the same order - `id`, `empresa_id`, `step`,\n`tipo_dte`, `kind`, `captured_at`, `retention_until`, `sha256`, `bytes`, `content_type`,\n`detail` - so `storage_path` is absent here exactly as it is absent there. `detail`\ntravels as its compact JSON, quoted.\n\nPaged internally and capped at 50,000 rows. When the cap is reached the response says so\nin `Facturia-Export-Complete: false` rather than serving a truncated file that looks\nwhole; `step` and `kind` are how you narrow an export that hit it.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The rendicion, as a CSV attachment.",
            "headers": {
              "Content-Disposition": {
                "description": "`attachment; filename=\"rendicion-<rut>-<date>.csv\"`.",
                "schema": {
                  "type": "string"
                }
              },
              "Facturia-Export-Complete": {
                "description": "`false` when the 50,000-row cap truncated the export.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true",
                    "false"
                  ]
                }
              },
              "Facturia-Export-Rows": {
                "description": "How many rows the file carries.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/automation_settings": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        }
      ],
      "get": {
        "tags": [
          "Empresas"
        ],
        "operationId": "getAutomationSettings",
        "summary": "Retrieve per-empresa automation settings",
        "description": "The empresa's automatic-emission toggles (`auto_characterize_purchases`, `auto_emit_46`, …), each defaulted where unset.",
        "security": [
          {
            "bearerAuth": [
              "empresas:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Automation settings.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutomationSettings"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "patch": {
        "tags": [
          "Empresas"
        ],
        "operationId": "updateAutomationSettings",
        "summary": "Update per-empresa automation settings",
        "description": "Every automation defaults to OFF except `auto_request_folios`. `auto_emit_46` and\n`auto_file_f29` are opt-in per empresa and capped.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AutomationSettings"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated settings.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutomationSettings"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/empresas/{empresa_id}/branding": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        }
      ],
      "get": {
        "tags": [
          "Empresas"
        ],
        "operationId": "getBranding",
        "summary": "Read logo, default print format and footer text",
        "description": "Every field is filled in from the platform defaults where the empresa has set nothing, so\nthe object always describes what a PDF will actually look like rather than only what was\noverridden.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The empresa's branding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Branding"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "patch": {
        "tags": [
          "Empresas"
        ],
        "operationId": "updateBranding",
        "summary": "Update logo, default print format and footer text",
        "description": "Update the panel and hosted-page branding fields (logo, colors, display name). An unknown field name is refused (`422 unknown_setting`), never silently ignored.",
        "security": [
          {
            "bearerAuth": [
              "empresas:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Branding"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated branding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Branding"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/folios": {
      "get": {
        "tags": [
          "Folios"
        ],
        "operationId": "listFolios",
        "summary": "Folio availability per doc type",
        "description": "Folio ranges (CAF-backed) available to the empresa, with how many of each `tipo_dte` remain before a new request is needed.",
        "security": [
          {
            "bearerAuth": [
              "empresas:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "name": "tipo_dte",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/TipoDte"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Folio ranges.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/FolioRange"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/folios/requests": {
      "post": {
        "tags": [
          "Folios"
        ],
        "operationId": "requestFolios",
        "summary": "Request a new CAF from the SII (async)",
        "description": "The SII's timbraje is a scripted portal flow, not a webservice. Returns a job.",
        "security": [
          {
            "bearerAuth": [
              "folios:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "tipo_dte"
                ],
                "properties": {
                  "tipo_dte": {
                    "$ref": "#/components/schemas/TipoDte"
                  },
                  "quantity": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Folios requested. Omit to let Facturia size it from consumption history."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/cafs": {
      "get": {
        "tags": [
          "Folios"
        ],
        "operationId": "listCafs",
        "summary": "List CAFs held for this empresa",
        "description": "Every CAF (folio authorization) uploaded for the empresa, with its range, `tipo_dte` and how many folios remain.",
        "security": [
          {
            "bearerAuth": [
              "empresas:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A list of CAFs.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Caf"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "post": {
        "tags": [
          "Folios"
        ],
        "operationId": "uploadCaf",
        "summary": "Upload a CAF you already hold",
        "description": "Upload a CAF file obtained from the SII's own portal. Rejected if its range overlaps one already on file for the same `tipo_dte`.",
        "security": [
          {
            "bearerAuth": [
              "folios:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "caf_xml"
                ],
                "properties": {
                  "caf_xml": {
                    "type": "string",
                    "description": "The raw `<AUTORIZACION>` XML as downloaded from the SII."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "CAF registered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Caf"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/cafs/{caf_id}": {
      "parameters": [
        {
          "name": "caf_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "examples": [
              "caf_01K2P4V8QW2M"
            ]
          }
        }
      ],
      "delete": {
        "tags": [
          "Folios"
        ],
        "operationId": "deactivateCaf",
        "summary": "Deactivate a CAF (history is preserved)",
        "description": "Deactivate a CAF (`active: false`). Folios already allocated from it are unaffected; the range simply stops offering new ones.",
        "security": [
          {
            "bearerAuth": [
              "folios:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "responses": {
          "204": {
            "description": "Deactivated."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/documents": {
      "get": {
        "tags": [
          "Documents"
        ],
        "operationId": "listDocuments",
        "summary": "List emitted documents",
        "description": "List documents for the empresa, newest first, filterable by `tipo_dte`, `status` and date range.",
        "security": [
          {
            "bearerAuth": [
              "documents:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "expand",
            "in": "query",
            "description": "Comma-separated, applied per returned page. On a LIST the vocabulary is narrower than\non `GET /documents/{document_id}`: `events`, `references` and `received_document` only.\n`xml` and `pdf_base64` inline a whole artifact per row and are `400 invalid_field`\nhere — fetch them from the single document, or from `/pdf` and `/xml` (§5.4).\n",
            "schema": {
              "type": "string",
              "examples": [
                "events,received_document"
              ]
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/DocumentStatus"
            }
          },
          {
            "name": "tipo_dte",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/TipoDte"
            }
          },
          {
            "name": "folio",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "receptor_rut",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/Rut"
            }
          },
          {
            "name": "external_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "issue_date[gte]",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "issue_date[lte]",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "created_at[gte]",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of documents.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Document"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "post": {
        "tags": [
          "Documents"
        ],
        "operationId": "createDocument",
        "summary": "Emit a DTE",
        "description": "Returns `201` with `status: queued` and **`folio: null`**. The folio arrives when the\ndocument reaches `sent`: it is allocated by the engine after the row exists, so no `201`\ncan carry one. `null` there is a WAIT, not a failure and not a placeholder — observe it\nwith `document.sent`, or by re-reading the document on the cadence\n`Facturia-Poll-After` gives you.\n\nThe SII round-trip after that is a state machine you watch the same way. Everything not\nsupplied is defaulted from the empresa profile (emisor block, giro, dirección), the\nreceptor registry, and the tipo's tax rules.\n",
        "security": [
          {
            "bearerAuth": [
              "documents:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DocumentCreateRequest"
              },
              "examples": {
                "minimalBoleta": {
                  "summary": "Minimal boleta (two fields)",
                  "value": {
                    "tipo_dte": 39,
                    "items": [
                      {
                        "description": "Servicio de mantención mensual",
                        "quantity": 1,
                        "unit_price": 25000
                      }
                    ]
                  }
                },
                "minimalFactura": {
                  "summary": "Minimal factura afecta (three fields)",
                  "value": {
                    "tipo_dte": 33,
                    "receptor": {
                      "rut": "76543212-K",
                      "giro": "Venta al por mayor de alimentos",
                      "address": {
                        "street": "Los Militares 5001, of. 302",
                        "comuna": "Las Condes",
                        "city": "Santiago"
                      }
                    },
                    "items": [
                      {
                        "description": "Desarrollo a medida — agosto",
                        "quantity": 1,
                        "unit_price": 2400000
                      }
                    ]
                  }
                },
                "fullFactura": {
                  "summary": "Full factura with references, discounts and asserted totals",
                  "value": {
                    "tipo_dte": 33,
                    "external_id": "order-8842",
                    "issue_date": "2026-08-14",
                    "prices_include_tax": false,
                    "receptor": {
                      "rut": "76543212-K",
                      "legal_name": "Comercial Andina Ltda.",
                      "giro": "Venta al por mayor de alimentos",
                      "address": {
                        "street": "Los Militares 5001, of. 302",
                        "comuna": "Las Condes",
                        "city": "Santiago"
                      },
                      "email_document_to": [
                        "pagos@andina.cl"
                      ]
                    },
                    "items": [
                      {
                        "description": "Licencia plataforma — plan Enterprise",
                        "quantity": 12,
                        "unit": "UN",
                        "unit_price": 180000,
                        "discount_percent": 5
                      },
                      {
                        "description": "Servicio de capacitación",
                        "quantity": 1,
                        "unit_price": 300000
                      }
                    ],
                    "references": [
                      {
                        "tipo_dte": 801,
                        "folio": "OC-99213",
                        "date": "2026-08-01",
                        "reason": "Orden de compra"
                      }
                    ],
                    "payment": {
                      "terms": "credito",
                      "due_date": "2026-09-13",
                      "method": "transferencia"
                    },
                    "totals": {
                      "net": 2352000,
                      "iva": 446880,
                      "exempt": 0,
                      "total": 2798880
                    },
                    "pdf": {
                      "cedible": true,
                      "public": true
                    },
                    "xml": {
                      "public": true
                    },
                    "metadata": {
                      "crm_deal": "D-1182"
                    }
                  }
                },
                "creditNote": {
                  "summary": "Nota de crédito correcting amounts",
                  "value": {
                    "tipo_dte": 61,
                    "receptor": {
                      "rut": "76543212-K",
                      "giro": "Venta al por mayor de alimentos",
                      "address": {
                        "street": "Los Militares 5001, of. 302",
                        "comuna": "Las Condes",
                        "city": "Santiago"
                      }
                    },
                    "references": [
                      {
                        "tipo_dte": 33,
                        "folio": 412,
                        "date": "2026-08-14",
                        "code": 3,
                        "reason": "Corrige montos"
                      }
                    ],
                    "items": [
                      {
                        "description": "Ajuste de precio",
                        "quantity": 1,
                        "unit_price": 120000
                      }
                    ]
                  }
                },
                "facturaDeCompra46": {
                  "summary": "Factura de compra for a foreign digital service",
                  "value": {
                    "tipo_dte": 46,
                    "receptor": {
                      "rut": "55555555-5",
                      "legal_name": "Google Cloud EMEA Ltd",
                      "foreign": true
                    },
                    "items": [
                      {
                        "description": "Google Ads — julio 2026",
                        "quantity": 1,
                        "unit_price": 1000000
                      }
                    ],
                    "withholding": {
                      "iva": "total"
                    }
                  }
                },
                "guiaDespacho": {
                  "summary": "Guía de despacho with a transport block",
                  "value": {
                    "tipo_dte": 52,
                    "receptor": {
                      "rut": "76543212-K",
                      "address": {
                        "street": "Ruta 5 Sur km 12",
                        "comuna": "San Bernardo",
                        "city": "Santiago"
                      }
                    },
                    "dispatch": {
                      "transfer_type": "venta",
                      "carrier": {
                        "rut": "11111111-1",
                        "name": "Transportes Sur",
                        "plate": "KJHZ42",
                        "driver_rut": "13456789-9",
                        "driver_name": "Ana Rojas"
                      },
                      "requester_rut": "76543212-K"
                    },
                    "items": [
                      {
                        "description": "Pallet de insumos",
                        "quantity": 4,
                        "unit_price": 120000
                      }
                    ]
                  }
                },
                "sandboxForcedRejection": {
                  "summary": "Test mode — force a rejection",
                  "value": {
                    "tipo_dte": 33,
                    "receptor": {
                      "rut": "76543212-K",
                      "giro": "Venta al por mayor de alimentos",
                      "address": {
                        "street": "Los Militares 5001, of. 302",
                        "comuna": "Las Condes",
                        "city": "Santiago"
                      }
                    },
                    "items": [
                      {
                        "description": "Consultoría",
                        "quantity": 1,
                        "unit_price": 1200000
                      }
                    ],
                    "test": {
                      "outcome": "rejected",
                      "reason_code": "TED-2-510",
                      "settle_after_ms": 3000
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The preview named by `preview_id` was ALREADY emitted, by this call's own earlier\nattempt or by a concurrent one. The original document, unchanged — nothing was created\nby this call and no second folio was spent (§10.2). Only reachable on a\n`{\"preview_id\": \"prev_…\"}` body.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmittedDocument"
                }
              }
            }
          },
          "201": {
            "description": "Document created and queued for transmission.\n\nCarries `Facturia-Poll-After` when the document is not terminal — which the `201`\nalways is on the live lane, since it answers `status: \"queued\"`. It was attached only\nby `GET /v1/documents/{document_id}` until 2026-09-04, so an agent following «poll per\n`Facturia-Poll-After`» had to issue a first read purely to discover the number it was\ntold to poll with (#522). Same value, same function, both doors.\n",
            "headers": {
              "Facturia-Poll-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds until the next useful read of this document. Absent when the document is\nalready terminal (a sandbox emission is `accepted` immediately).\n"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmittedDocument"
                },
                "examples": {
                  "default": {
                    "summary": "A minimal factura, just accepted for emission.",
                    "value": {
                      "id": "doc_01K2R7Q3T5NX8B",
                      "object": "document",
                      "mode": "live",
                      "empresa": {
                        "id": "emp_01K2R6ZC4P8QW1VN7T3MHDY9EK",
                        "rut": "77928532-4"
                      },
                      "external_id": null,
                      "tipo_dte": 33,
                      "folio": null,
                      "issue_date": "2026-09-02",
                      "status": "queued",
                      "receptor": {
                        "rut": "76543212-K",
                        "legal_name": "Comercial Andina Ltda."
                      },
                      "items": [
                        {
                          "description": "Desarrollo a medida — agosto",
                          "quantity": 1,
                          "unit_price": 2400000
                        }
                      ],
                      "totals": {
                        "net": 2400000,
                        "exempt": 0,
                        "iva": 456000,
                        "other_taxes": 0,
                        "total": 2856000
                      },
                      "references": [],
                      "pdf_url": "https://api.facturia.cl/v1/documents/doc_01K2R7Q3T5NX8B/pdf",
                      "xml_url": "https://api.facturia.cl/v1/documents/doc_01K2R7Q3T5NX8B/xml",
                      "sii": {
                        "environment": "produccion",
                        "track_id": null,
                        "estado": null,
                        "glosa": null,
                        "err_code": null,
                        "num_atencion": null,
                        "modified_by": [],
                        "received_at": null,
                        "last_checked_at": null
                      },
                      "cesion": {
                        "cedible": null,
                        "sii_status_code": null,
                        "checked_at": null
                      },
                      "rejection_reasons": [],
                      "annulled_by": null,
                      "metadata": {},
                      "created_at": "2026-09-02T15:04:11Z",
                      "accepted_at": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "description": "Well-formed, and refused by a domain rule. The codes this operation answers, in the\norder the door checks them:\n\n- `empresa_not_emitting` — **live only**: this endpoint requires\n  `onboarding.status = emitting`, and the empresa has not got there yet. In SANDBOX it\n  is not checked at all: a test-mode empresa emits from the first minute, which is what\n  makes the whole surface exercisable before the SII has issued anything.\n  `error.details` is an `EmissionRefusalDetails` and carries the `pending_action` —\n  who must act, where, and in what words — so the refusal itself is the handoff.\n- `empresa_not_active` — the empresa is `suspended` or `archived`.\n- `tipo_not_available_for_channel` — this `tipo_dte` is not emitted on this empresa's\n  `emission_channel` (§6.2).\n- plus the payload rules: `totals_mismatch`, `receptor_incomplete`,\n  `reference_required` and their siblings — see `GET /error_codes`.\n\nThis list named folios_exhausted until 2026-09-04. There is no such code and there\nnever was: running out of folios is not refused at this door at all — the range is\ndrawn from AFTER the request is accepted, so it arrives as a `rejected` document or,\non `POST /documents/previews`, as the non-blocking warning `no_folios_available`\n(#496 found it by cross-checking every code these sentences name against the published\ndictionary).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/documents/previews": {
      "post": {
        "tags": [
          "Documents"
        ],
        "operationId": "createDocumentPreview",
        "summary": "Price and validate a document without emitting it",
        "description": "Runs the ENTIRE emission validator — the same refusals, in the same words — and then\nperforms none of the emission. `folio` is `null` because none was taken; `folio_preview` is\nthe folio that WOULD be used, read from the counter and not reserved.\n\nStored for an hour and readable with `GET /documents/previews/{preview_id}`, which returns\nwhat was computed rather than re-pricing: totals that moved between being shown and being\napproved is the failure this flow exists to prevent.\n\nEmit it by sending `{\"preview_id\": \"prev_…\"}` to `POST /documents`. Emitting the same\npreview twice returns the ORIGINAL document with `200` and spends no second folio — a\nproperty enforced in the database, because an agent retrying a tool call is the ordinary\ncase here and agents rarely send an `Idempotency-Key`.\n",
        "security": [
          {
            "bearerAuth": [
              "documents:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The same body `POST /documents` takes.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DocumentCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The computed preview. Nothing was emitted and no folio was reserved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentPreview"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/documents/previews/{preview_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/PreviewId"
        }
      ],
      "get": {
        "tags": [
          "Documents"
        ],
        "operationId": "getDocumentPreview",
        "summary": "Retrieve a stored preview",
        "description": "Returns what was computed, never a re-computation. A preview that has been emitted stays\nreadable with `emitted: true` and a `consumed_at`; an unconsumed one is gone an hour after\nit was created and answers `404`.\n",
        "security": [
          {
            "bearerAuth": [
              "documents:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "The preview.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentPreview"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/documents/{document_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "get": {
        "tags": [
          "Documents"
        ],
        "operationId": "getDocument",
        "summary": "Retrieve a document",
        "description": "One document: its lifecycle `status`, totals, `public_links` if any are live, and `rejection_reasons` if the SII refused it.",
        "security": [
          {
            "bearerAuth": [
              "documents:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "name": "expand",
            "in": "query",
            "description": "Comma-separated. One or more of `xml`, `pdf_base64`, `events`, `references`,\n`received_document`. An unrecognised value is `400 invalid_field`.\n\n`received_document` is populated when this document was emitted in response to a\nsupplier's — matched at emission by reference (tipo_dte, folio) plus emisor RUT =\nreceptor RUT, within the same empresa, and only when that match is unique (§5.4).\n",
            "schema": {
              "type": "string",
              "examples": [
                "xml,events"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The document.",
            "headers": {
              "Facturia-Poll-After": {
                "description": "Seconds until the next useful poll, from our own scheduler.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                },
                "examples": {
                  "default": {
                    "summary": "The same document, after the SII accepted it.",
                    "value": {
                      "id": "doc_01K2R7Q3T5NX8B",
                      "object": "document",
                      "mode": "live",
                      "empresa": {
                        "id": "emp_01K2R6ZC4P8QW1VN7T3MHDY9EK",
                        "rut": "77928532-4"
                      },
                      "external_id": "factura-0842",
                      "tipo_dte": 33,
                      "folio": 1024,
                      "issue_date": "2026-09-02",
                      "status": "accepted",
                      "receptor": {
                        "rut": "76543212-K",
                        "legal_name": "Comercial Andina Ltda."
                      },
                      "items": [
                        {
                          "description": "Desarrollo a medida — agosto",
                          "quantity": 1,
                          "unit_price": 2400000
                        }
                      ],
                      "totals": {
                        "net": 2400000,
                        "exempt": 0,
                        "iva": 456000,
                        "other_taxes": 0,
                        "total": 2856000
                      },
                      "references": [],
                      "pdf_url": "https://api.facturia.cl/v1/documents/doc_01K2R7Q3T5NX8B/pdf",
                      "xml_url": "https://api.facturia.cl/v1/documents/doc_01K2R7Q3T5NX8B/xml",
                      "sii": {
                        "environment": "produccion",
                        "track_id": "926194837",
                        "estado": "EPR",
                        "glosa": "Envio Procesado",
                        "err_code": null,
                        "num_atencion": null,
                        "modified_by": [],
                        "received_at": "2026-09-02T15:06:40Z",
                        "last_checked_at": "2026-09-02T15:07:02Z"
                      },
                      "cesion": {
                        "cedible": null,
                        "sii_status_code": null,
                        "checked_at": null
                      },
                      "rejection_reasons": [],
                      "annulled_by": null,
                      "metadata": {},
                      "created_at": "2026-09-02T15:04:11Z",
                      "accepted_at": "2026-09-02T15:07:02Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/documents/{document_id}/pdf": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "get": {
        "tags": [
          "Documents"
        ],
        "operationId": "getDocumentPdf",
        "summary": "Download the PDF",
        "description": "The document's rendered PDF (`?copy=original` or `cedible` where Ley 19.983 gives one). `409 artifact_not_ready` if the SII has not accepted the document yet — the representación impresa is rendered on acceptance, not before.",
        "security": [
          {
            "bearerAuth": [
              "documents:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "name": "copy",
            "in": "query",
            "description": "Which printed copy to serve. `cedible` is available for facturas — tipo_dte 33 and 34\n(Ley 19.983 art. 1; Res. Ex. SII N°14/2005 §1°; Manual de Muestras Impresas §1.4). Any\nother type returns `422 cedible_not_available`.\n\n`cedible` does not exist AT ALL on an `emission_channel=sfg` empresa, on any tipo: the\nSII signs and proxies its own PDF for that channel, and its bytes carry no\nacuse-de-recibo box or CEDIBLE legend to serve as one. `copy=cedible` there returns\n`422 cedible_not_available_for_channel`; `copy=original` is unaffected.\n\nThe two copies are two separate files: the original carries no acuse-de-recibo box and\nno CEDIBLE legend, the cedible copy carries both. `both` was withdrawn in wave 9 — the\nmuestras manual allows exactly one page per file (p.12) — and now returns\n`400 invalid_field`, as does any other unrecognised value.\n\n`format` (`carta` | `80mm`) was also withdrawn: no thermal layout exists and none was\never served. Supplying it returns `400 invalid_field`.\n\n**Omitting it** does not always mean `original`. The default is resolved in order: this\ndocument's own `pdf.cedible` (set at emission, §9.3) if it named one; else the empresa's\n`branding.cedible_by_default` (§7.6); else `original` — and never `cedible` on the SFG\nchannel, regardless of either default. Send `?copy=` explicitly to override either\ndefault for one read.\n",
            "schema": {
              "type": "string",
              "enum": [
                "original",
                "cedible"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The rendered PDF.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/documents/{document_id}/xml": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "get": {
        "tags": [
          "Documents"
        ],
        "operationId": "getDocumentXml",
        "summary": "Download the signed XML exactly as sent to the SII",
        "description": "The exact XML Facturia sent to the SII (or will send), for a caller that wants the wire bytes rather than the parsed object.",
        "security": [
          {
            "bearerAuth": [
              "documents:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "The signed EnvioDTE / EnvioBOLETA XML (ISO-8859-1).",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/documents/{document_id}/events": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "get": {
        "tags": [
          "Documents"
        ],
        "operationId": "listDocumentEvents",
        "summary": "Lifecycle events for one document",
        "description": "The document's own event history — every `document.*` webhook Facturia published for it, in order, independent of whether delivery succeeded.",
        "security": [
          {
            "bearerAuth": [
              "documents:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "Events, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/DocumentEvent"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/documents/{document_id}/retry": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "post": {
        "tags": [
          "Documents"
        ],
        "operationId": "retryDocument",
        "summary": "Retry a document in `failed`",
        "description": "Reuses the already-assigned folio. Never creates a second document.",
        "security": [
          {
            "bearerAuth": [
              "documents:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "The document, back in `queued`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/documents/{document_id}/send": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "post": {
        "tags": [
          "Documents"
        ],
        "operationId": "sendDocument",
        "summary": "Email the document to the receptor",
        "description": "`202` when THIS call created the send; `200` with the SAME job when an identical one\n(same document, same addresses, same attachments) already exists — one email, one job id,\nmirroring §10.2's \"emitting the same preview twice returns the original document\". Both\nstatuses carry the same body, so a client that only handles one of them is handling half\nthe endpoint.\n",
        "security": [
          {
            "bearerAuth": [
              "documents:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "email"
                    }
                  },
                  "attach": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "pdf",
                        "xml"
                      ]
                    },
                    "default": [
                      "pdf",
                      "xml"
                    ],
                    "description": "Always the ORIGINAL copy of the PDF — never cedible, whatever this document's\nor the empresa's default says. §9.6: the cedible copy is an instrument for\nassigning the debt, not the invoice you send a customer to be paid.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "An identical send already exists. The ORIGINAL job — no second email.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendJob"
                }
              }
            }
          },
          "202": {
            "description": "Queued for delivery.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendJob"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/documents/{document_id}/cesion": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "post": {
        "tags": [
          "Documents"
        ],
        "operationId": "checkDocumentCesion",
        "summary": "Ask the SII whether this factura is cedible",
        "description": "READ-ONLY (ticket #47). Queues a query against the SII's own registro\n(`WSREGISTRORECLAMODTE.consultarDocDteCedible`) and caches the answer on the document's\n`cesion` object. Nothing here cedes the invoice, builds an AEC, or writes to the Registro\nde Cesión de Créditos — that operation takes no action code and cannot change SII state.\n\nUnder Ley 19.983 a factura becomes assignable when the receptor gives acuse de recibo, or\nwhen eight días corridos pass without a reclamo; one declared as paid al contado never\ndoes (Circular N°35 de 23.06.2017). Only the SII knows which of those applies, so this is\na report, never a computation.\n\n`202` when this call queued the question, `200` when an identical one is already in\nflight. Poll `GET /documents/{document_id}` for the answer; there is no webhook.\n\nAccepts a `documents:read` key: the caller this exists for is a factoring integration,\nwhich should not need one that can issue invoices.\n\nIn test mode the answer is simulated in-process against the empresa's simulated clock and\nno SII socket is opened — `24` until eight days after acceptance, `25` after.\n",
        "security": [
          {
            "bearerAuth": [
              "documents:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "A check against this same answer is already queued (or, in test mode, the simulated\nanswer, already applied). The document, with its current `cesion`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                }
              }
            }
          },
          "202": {
            "description": "Queued. The document, with `cesion` still showing the previously known answer — null\nthroughout if this was the first check.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/documents/{document_id}/cede": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "post": {
        "tags": [
          "Documents"
        ],
        "operationId": "cedeDocument",
        "summary": "Cede this factura to a factoring company, registering it in the SII's RTC",
        "description": "THE WRITE (ticket #60), unlike `POST …/cesion` above (a read). Assembles an Archivo\nElectrónico de Cesión — the emisor's original signed DTE plus the terms of the transfer,\nsigned by the cedente — and uploads it to the SII's Registro Electrónico de Transferencia\nde Crédito. A real, irreversible act.\n\nWorks the same regardless of channel (ticket #60, ruled 2026-09-06): the AEC carries the\nemisor's original signed envelope verbatim, so it does not care whether the DTE was emitted\nthrough `emission_channel=sfg` or `emission_channel=mercado`. `cesion_channel_unsupported`,\nthe code the old channel guard raised, is RETIRED from the catalogue.\n\n`202` with a `job` when this call queued a NEW attempt; `200` with the SAME job when one\nis already `queued`/`submitted`/`aec_generated` for this document (one attempt in flight\nat a time). `GET /documents/{document_id}` carries the newest attempt's outcome under\n`cesion.status`.\n\nA RECEIPT IS NOT A REGISTRATION (ticket #69). The furthest the upload itself can take an\nattempt is `submitted` — the SII answered «Envío recibido OK» and issued a TrackID. Its\nvalidation happens afterwards, and a rejection is notified by e-mail to the cedente hours\nlater, so this API claims `confirmed` only when the registry's own\n`wsRPETCConsulta.getEstEnvio` answers `EOK` («Envío Aceptado») for that TrackID. Webhooks\nfollow that exactly: `document.cesion_submitted` means «AEC recibido, anotación\npendiente», `document.cesion_confirmed` is the one to settle money on, and\n`document.cesion_failed` covers a refusal, a fence, an answer nobody recognises, and a\nregistration that could never be confirmed.\n\n`409 cesion_attempt_may_have_registered` when the newest attempt for this document had\nalready put the signed AEC on the wire when it stopped (ticket #76). Whether it registered\ncannot be decided from here, so a second cesión is refused rather than risking the same\ncredit being assigned twice. `error.details` carries this system's own last known registry\nanswer for that attempt (`rtc_estado`, `rtc_track_id`) when one was ever recorded — reading\nit costs a column, not a new SII call; when neither was ever recorded the details say so.\n\nIn test mode this settles in the same request (§4.1) — no socket opened, the attempt\nlands `simulated`, and BOTH `document.cesion_submitted` and `document.cesion_confirmed`\nfire, in that order, so the event a factoring integration acts on is exercisable in test.\n\nLive submission is owner-gated, and since the 2026-09-06 ruling (R6) also gated per\nempresa: the AEC assembly and the registry upload are built and checked against the SII's\nown schema — the first live upload (2026-09-03) was accepted by the registry with a real\nTrackID and then rejected at validation for a signature defect, since fixed. Opening it\nneeds BOTH `ALLOW_LIVE_CESION_SUBMITS` (an operator's own shell, never a deploy) AND the\nceding empresa's own connection reaching `onboarding_status=emitting`, `active`,\n`ambiente=palena` and not a sandbox connection. A live attempt missing either one is\naccepted and queued like any other and settles `failed` non-retryably, naming whichever\ncondition is missing.\n",
        "security": [
          {
            "bearerAuth": [
              "documents:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "cesionario",
                  "ultimo_vencimiento"
                ],
                "properties": {
                  "cesionario": {
                    "type": "object",
                    "required": [
                      "rut",
                      "razon_social",
                      "direccion",
                      "email"
                    ],
                    "properties": {
                      "rut": {
                        "$ref": "#/components/schemas/Rut"
                      },
                      "razon_social": {
                        "type": "string"
                      },
                      "direccion": {
                        "type": "string",
                        "minLength": 5
                      },
                      "email": {
                        "type": "string",
                        "format": "email"
                      }
                    }
                  },
                  "monto": {
                    "type": "integer",
                    "description": "CLP, in `[1, total]`. Defaults to the document's total."
                  },
                  "ultimo_vencimiento": {
                    "type": "string",
                    "format": "date"
                  },
                  "otras_condiciones": {
                    "type": "string"
                  },
                  "email_deudor": {
                    "type": "string",
                    "format": "email"
                  },
                  "contacto": {
                    "type": "object",
                    "properties": {
                      "nombre": {
                        "type": "string"
                      },
                      "fono": {
                        "type": "string"
                      },
                      "email": {
                        "type": "string",
                        "format": "email"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A cesión attempt was already in flight for this document. The same job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CesionJob"
                }
              }
            }
          },
          "202": {
            "description": "Queued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CesionJob"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/documents/{document_id}/credit_note": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "post": {
        "tags": [
          "Documents"
        ],
        "operationId": "createCreditNoteForDocument",
        "summary": "Emit a nota de crédito against this document",
        "description": "There is no \"delete an invoice\" in Chile. `code: 1` annuls the referenced document,\n`2` corrects text, `3` corrects amounts (supply `items` for the corrected values).\n",
        "security": [
          {
            "bearerAuth": [
              "documents:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code",
                  "reason"
                ],
                "properties": {
                  "code": {
                    "type": "integer",
                    "enum": [
                      1,
                      2,
                      3
                    ]
                  },
                  "reason": {
                    "type": "string",
                    "maxLength": 90
                  },
                  "items": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/DocumentItem"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The nota de crédito (tipo 61).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/documents/{document_id}/public_links": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "post": {
        "tags": [
          "Documents"
        ],
        "operationId": "createDocumentPublicLinks",
        "summary": "Create or rotate public links for this document's artifacts",
        "description": "Turns a public link on after the fact, or rotates an existing one (which invalidates the\nprevious URL). A link exposes exactly one artifact of one document — never the empresa's\nother documents, never account data — and cannot be escalated into an API credential.\n\n`cedible` (ticket #18 wave 10) is NOT in the default set: unlike `pdf`/`xml` it does not\nexist for every document, so a caller who did not ask for it does not get one minted\nsilently — mirroring `?copy=` on `GET …/pdf`, which defaults to `original` rather than\nguessing. Requesting it for a tipo outside 33/34 is `422 cedible_not_available`; requesting\nit on an `emission_channel=sfg` empresa, on any tipo, is `422 cedible_not_available_for_channel`\n(#48) — the SII proxies its own PDF for that channel and no cedible copy of it ever exists,\nso this refuses the mint rather than handing back a `.cedible` link that could only ever\nresolve to the non-cedible file.\n",
        "security": [
          {
            "bearerAuth": [
              "documents:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "artifacts"
                ],
                "properties": {
                  "artifacts": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "string",
                      "enum": [
                        "pdf",
                        "xml",
                        "cedible"
                      ]
                    },
                    "default": [
                      "pdf",
                      "xml"
                    ]
                  },
                  "expires_in_days": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 1,
                    "maximum": 365,
                    "default": 90,
                    "description": "Null means the link does not expire."
                  },
                  "expires_in_seconds": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "description": "The same lifetime in seconds, clamped to 30s–365d. WINS when both units are\nsent: days cannot express the ten-minute link an agent needs, and the shorter,\nmore specific unit is the safer reading of an ambiguous request.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The minted links. NOT the document: the secret half of each URL is stored hashed, so\nthis response is the only place a working URL ever exists. Capture it.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicLinks"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "delete": {
        "tags": [
          "Documents"
        ],
        "operationId": "revokeDocumentPublicLinks",
        "summary": "Revoke every public link for this document",
        "description": "Revoke every live public link minted for the document. Already-cached copies of the URL keep working until they expire; this only stops new fetches.",
        "security": [
          {
            "bearerAuth": [
              "documents:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked. Existing URLs stop resolving immediately."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/received_documents": {
      "get": {
        "tags": [
          "Received documents"
        ],
        "operationId": "listReceivedDocuments",
        "summary": "List supplier DTEs received for this empresa",
        "description": "Supplier DTEs that arrived at the empresa's reception address, newest first, filterable by `tipo_dte` and acceptance `status`.",
        "security": [
          {
            "bearerAuth": [
              "received:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "expand",
            "in": "query",
            "description": "Accepts nothing on this list, and says so rather than ignoring it. `raw_xml` inlines\nthe supplier's entire signed envelope, so it is available on\n`GET /received_documents/{received_document_id}` only; any value here is\n`400 invalid_field` (§5.4).\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tipo_dte",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/TipoDte"
            }
          },
          {
            "name": "emisor_rut",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/Rut"
            }
          },
          {
            "name": "commercial_response_state",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/CommercialResponseState"
            }
          },
          {
            "name": "source",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "intercambio",
                "rcv",
                "manual"
              ]
            }
          },
          {
            "name": "issue_date[gte]",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "issue_date[lte]",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of received documents.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ReceivedDocument"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/received_documents/{received_document_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ReceivedDocumentId"
        }
      ],
      "get": {
        "tags": [
          "Received documents"
        ],
        "operationId": "getReceivedDocument",
        "summary": "Retrieve a received document",
        "description": "One received DTE, including whatever commercial response (`accept`/`reject`/`acknowledge_receipt`) has been registered for it.",
        "security": [
          {
            "bearerAuth": [
              "received:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "name": "expand",
            "in": "query",
            "description": "`raw_xml` returns the supplier's original XML inline, signature intact.",
            "schema": {
              "type": "string",
              "examples": [
                "raw_xml"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The received document.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReceivedDocument"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/received_documents/{received_document_id}/xml": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ReceivedDocumentId"
        }
      ],
      "get": {
        "tags": [
          "Received documents"
        ],
        "operationId": "getReceivedDocumentXml",
        "summary": "Download the original supplier XML",
        "description": "The exact XML the SII delivered for this received document.",
        "security": [
          {
            "bearerAuth": [
              "received:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "The XML as received. This file is the legal artifact.",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/received_documents/{received_document_id}/pdf": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ReceivedDocumentId"
        }
      ],
      "get": {
        "tags": [
          "Received documents"
        ],
        "operationId": "getReceivedDocumentPdf",
        "summary": "Render the received document as PDF",
        "description": "A rendered PDF of the received document, for display or archival outside the API.",
        "security": [
          {
            "bearerAuth": [
              "received:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "The rendered PDF.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/received_documents/{received_document_id}/accept": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ReceivedDocumentId"
        }
      ],
      "post": {
        "tags": [
          "Received documents"
        ],
        "operationId": "acceptReceivedDocument",
        "summary": "Accept the content (SII acción ACD)",
        "description": "Types 33/34/43 only, inside the 8-day window, once. Doing nothing is also a valid,\nlegally meaningful choice: silence accepts on day 8.\n",
        "security": [
          {
            "bearerAuth": [
              "received:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "note": {
                    "type": "string",
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The registered commercial response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommercialResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/received_documents/{received_document_id}/reject": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ReceivedDocumentId"
        }
      ],
      "post": {
        "tags": [
          "Received documents"
        ],
        "operationId": "rejectReceivedDocument",
        "summary": "Reclamar the document (RCD / RFP / RFT)",
        "description": "Irreversible. There is no un-reclamar.",
        "security": [
          {
            "bearerAuth": [
              "received:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string",
                    "enum": [
                      "content",
                      "partial_missing",
                      "total_missing"
                    ],
                    "description": "content → RCD, partial_missing → RFP, total_missing → RFT."
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The registered commercial response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommercialResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/received_documents/{received_document_id}/acknowledge_receipt": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ReceivedDocumentId"
        }
      ],
      "post": {
        "tags": [
          "Received documents"
        ],
        "operationId": "acknowledgeReceiptOfReceivedDocument",
        "summary": "Otorgar recibo de mercaderías o servicios (SII acción ERM)",
        "description": "Register the `acuse de recibo` — receipt of the goods or service, distinct from `accept`/`reject`'s commercial acceptance — inside the statutory 8-day window.",
        "security": [
          {
            "bearerAuth": [
              "received:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "description": "The registered commercial response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommercialResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/purchases": {
      "get": {
        "tags": [
          "Tax"
        ],
        "operationId": "listPurchases",
        "summary": "RCV — registro de compras",
        "description": "The purchases ledger (Registro de Compras) synced from the SII, filterable by period, with each entry's `estado_contable` and any `characterize` outcome.",
        "security": [
          {
            "bearerAuth": [
              "tax:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Period"
          },
          {
            "name": "estado_contable",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/EstadoContable"
            }
          },
          {
            "name": "tipo_dte",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/TipoDte"
            }
          },
          {
            "name": "counterparty_rut",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/Rut"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "RCV purchase entries, with sync freshness metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RcvEntryList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/sales": {
      "get": {
        "tags": [
          "Tax"
        ],
        "operationId": "listSales",
        "summary": "RCV — registro de ventas",
        "description": "The sales ledger (Registro de Ventas) synced from the SII, filterable by period.",
        "security": [
          {
            "bearerAuth": [
              "tax:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Period"
          },
          {
            "name": "tipo_dte",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/TipoDte"
            }
          },
          {
            "name": "counterparty_rut",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/Rut"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "RCV sales entries.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RcvEntryList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/summary": {
      "get": {
        "tags": [
          "Tax"
        ],
        "operationId": "getTaxSummary",
        "summary": "Aggregated RCV totals for a period (the F29 inputs)",
        "description": "Period totals across purchases and sales — the numbers an F29 proposal is built from, before any characterization is applied.",
        "security": [
          {
            "bearerAuth": [
              "tax:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/PeriodRequired"
          }
        ],
        "responses": {
          "200": {
            "description": "Aggregation by direction and doc type.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaxSummary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/sync": {
      "post": {
        "tags": [
          "Tax"
        ],
        "operationId": "syncTaxData",
        "summary": "Force an RCV resync (async)",
        "description": "Trigger a fresh pull of the purchases and sales ledgers from the SII for the empresa, for ONE\nperiod. Returns a `job` (`type: rcv_sync`) to poll on `GET /jobs/{job_id}`; the ledger updates\nonly once it succeeds, and `result` then carries what was read.\n\n**`period` defaults to the previous month** (America/Santiago), and the job always reports the\nperiod it read in `result.period`. The daily sync reads the last three months, and the first\nsweep after an empresa's first successful sync backfills the twelve months before that; anything\nolder has NEVER been read unless somebody asked for it here — a `sync.last_synced_at` of `null`\non `GET /tax/sales?period=…` is how you can tell. To reach further back, call this once per\nperiod; the bucket is 10 requests per minute per empresa.\n\n**Force means force.** A call while a sync for the same period is still queued or running\nattaches to that job rather than queueing a second one; a call after it has finished queues a\nfresh read, whatever the daily sync did this morning.\n",
        "security": [
          {
            "bearerAuth": [
              "tax:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "period": {
                    "$ref": "#/components/schemas/Period"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Sync job accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/purchases/characterize": {
      "post": {
        "tags": [
          "Tax"
        ],
        "operationId": "characterizePurchasesBulk",
        "summary": "Caracterizar up to 500 purchase entries in one call",
        "description": "Characterize purchase-ledger entries in bulk (`entry_ids[]` plus the shared `estado_contable`/tax treatment), for the common case of clearing a whole period at once.",
        "security": [
          {
            "bearerAuth": [
              "tax:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "entries"
                ],
                "properties": {
                  "entries": {
                    "type": "array",
                    "maxItems": 500,
                    "items": {
                      "allOf": [
                        {
                          "type": "object",
                          "required": [
                            "id"
                          ],
                          "properties": {
                            "id": {
                              "type": "string"
                            }
                          }
                        },
                        {
                          "$ref": "#/components/schemas/Characterization"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Per-entry results.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "characterization_batch"
                    },
                    "updated": {
                      "type": "integer"
                    },
                    "failed": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "error": {
                            "$ref": "#/components/schemas/ErrorBody"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/purchases/{entry_id}/characterize": {
      "parameters": [
        {
          "$ref": "#/components/parameters/RcvEntryId"
        }
      ],
      "post": {
        "tags": [
          "Tax"
        ],
        "operationId": "characterizePurchase",
        "summary": "Caracterizar one purchase entry (writes to the SII registry)",
        "description": "Characterize one purchase-ledger entry — its `estado_contable` and whether its IVA is recoverable — overriding the SII's own proposal where the empresa's own accounting disagrees.",
        "security": [
          {
            "bearerAuth": [
              "tax:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Characterization"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated entry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RcvEntry"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/purchases/{entry_id}/characterize/revert": {
      "parameters": [
        {
          "$ref": "#/components/parameters/RcvEntryId"
        }
      ],
      "post": {
        "tags": [
          "Tax"
        ],
        "operationId": "revertPurchaseCharacterization",
        "summary": "Undo the last settled caracterización on a purchase",
        "description": "Queues a NEW caracterización restoring the `previous_tipo_compra` recorded on the last\nsettled action for this purchase, linked to it by `reverts`. It does not edit or delete the\noriginal: a write to a tax register that happened, happened, and its row is the evidence.\n\nAn undo of a write to the SII is itself a write to the SII, so it passes the identical\nopt-in gate and the identical confirmation two-step. `409 characterization_nothing_to_revert`\nwhen no settled action exists, or when the last one recorded no previous value.\n\nReverting to `no_recuperable` needs an `iva_no_recuperable_code`: the SII does not accept one\nwithout a reason, and the reason the earlier characterization used is only recoverable when a\nprior action on the same document recorded it.\n",
        "security": [
          {
            "bearerAuth": [
              "tax:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "iva_no_recuperable_code": {
                    "type": "integer",
                    "enum": [
                      1,
                      2,
                      3,
                      4,
                      9
                    ],
                    "description": "Required only when the value being restored is `no_recuperable`."
                  },
                  "confirmation_id": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The queued revert action.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CharacterizationAction"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/purchases/{entry_id}/estado_contable": {
      "parameters": [
        {
          "$ref": "#/components/parameters/RcvEntryId"
        }
      ],
      "post": {
        "tags": [
          "Tax"
        ],
        "operationId": "setEstadoContable",
        "summary": "Move an entry between REGISTRO / PENDIENTE / NO_INCLUIR",
        "description": "Records a LOCAL bookkeeping estado for the purchase. It is written to\n`estado_contable_override` and never over `estado_contable`, which belongs to the SII's own\nregister and is refreshed by every sync.\n\nThe F29 propuesta ignores the override unless the empresa's\n`honor_estado_contable_override` automation setting is on; the response's\n`estado_contable_effective` says which value the propuesta will use. Every write is recorded\nwith the key that made it and the SII value it did not replace.\n\nChanging an entry's caracterización at the SII is a different act, with a different\nendpoint (`POST /tax/purchases/{entry_id}/characterize`).\n",
        "security": [
          {
            "bearerAuth": [
              "tax:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "estado_contable"
                ],
                "properties": {
                  "estado_contable": {
                    "$ref": "#/components/schemas/EstadoContable"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated entry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RcvEntry"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/f29/proposals": {
      "get": {
        "tags": [
          "Tax"
        ],
        "operationId": "listF29Proposals",
        "summary": "List the periods that have a computed propuesta",
        "description": "A SPEC ADDITION over §12.3, which documents only the per-period read. Without it a caller\nreconciling a year, or a dashboard drawing twelve months, has to guess which periods exist\nand probe each one — twelve requests, eleven of them `404`.\n\nThe rows omit `validation` and `filing_id`: both are one query per proposal, and a caller\nscanning a year is asking \"which months are computed\", which `status` already answers. Read\nthe single propuesta for those.\n",
        "security": [
          {
            "bearerAuth": [
              "tax:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A list of F29 propuestas, newest period first.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/F29Proposal"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/f29/proposals/{period}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/PeriodPath"
        }
      ],
      "get": {
        "tags": [
          "Tax"
        ],
        "operationId": "getF29Proposal",
        "summary": "Computed F29 propuesta for a period",
        "description": "Computed by Facturia from the synced RCV plus the empresa's PPM and remanente parameters.\nThe SII pre-fills its own F29 for its own portal but publishes no API for it.\n",
        "security": [
          {
            "bearerAuth": [
              "tax:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "The proposal.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/F29Proposal"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/f29/proposals/{period}/upload_file": {
      "parameters": [
        {
          "$ref": "#/components/parameters/PeriodPath"
        }
      ],
      "get": {
        "tags": [
          "Tax"
        ],
        "operationId": "getF29UploadFile",
        "summary": "Download the SII-format upload .txt for this proposal",
        "description": "The SII's own upload-ready `.txt` for this F29 period, generated from the same proposal `GET /tax/f29/proposals/{period}` returns.",
        "security": [
          {
            "bearerAuth": [
              "tax:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "Fixed-format upload file, ready to submit manually if you prefer.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/f29/proposals/{period}/public_links": {
      "parameters": [
        {
          "$ref": "#/components/parameters/PeriodPath"
        }
      ],
      "post": {
        "tags": [
          "Tax"
        ],
        "operationId": "createF29ProposalPublicLinks",
        "summary": "Mint a no-auth link to the propuesta's upload .txt",
        "description": "`upload_file_url` needs a bearer key, which makes it useless as an MCP `resource_link` —\nmost MCP hosts cannot attach the caller's OAuth token when dereferencing one. This mints the\nsame kind of signed, unguessable, expiring link a document's artifacts get (§9.6), serving\nthe identical ISO-8859-1 bytes.\n\n`tax:read`, not `tax:write`: minting a link publishes nothing and changes no tax position.\n",
        "security": [
          {
            "bearerAuth": [
              "tax:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "artifacts": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "upload_file"
                      ]
                    },
                    "default": [
                      "upload_file"
                    ]
                  },
                  "expires_in_days": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 1,
                    "maximum": 365,
                    "default": 90
                  },
                  "expires_in_seconds": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "description": "Clamped to 30s–365d. Wins when both units are sent."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The minted link. Capture it — the secret half is stored hashed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicLinks"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "delete": {
        "tags": [
          "Tax"
        ],
        "operationId": "revokeF29ProposalPublicLinks",
        "summary": "Revoke every public link for this propuesta",
        "description": "Revoke every live public link minted for this F29 proposal's upload file.",
        "security": [
          {
            "bearerAuth": [
              "tax:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked. Existing URLs stop resolving immediately."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/f29/proposals/{period}/recompute": {
      "parameters": [
        {
          "$ref": "#/components/parameters/PeriodPath"
        }
      ],
      "post": {
        "tags": [
          "Tax"
        ],
        "operationId": "recomputeF29Proposal",
        "summary": "Recompute the proposal, optionally overriding codes",
        "description": "Rebuild the F29 proposal from the ledgers and any characterizations recorded since it was last computed. Does not file anything with the SII.",
        "security": [
          {
            "bearerAuth": [
              "tax:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "overrides": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "code",
                        "value"
                      ],
                      "properties": {
                        "code": {
                          "type": "integer"
                        },
                        "value": {
                          "type": "integer"
                        },
                        "reason": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The recomputed proposal.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/F29Proposal"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/f29/filings": {
      "get": {
        "tags": [
          "Tax"
        ],
        "operationId": "listF29Filings",
        "summary": "List F29 filings",
        "description": "F29 filings recorded for the empresa, newest first, each naming the period and the proposal it was filed from.",
        "security": [
          {
            "bearerAuth": [
              "tax:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "period[gte]",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of filings.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/F29Filing"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "post": {
        "tags": [
          "Tax"
        ],
        "operationId": "createF29Filing",
        "summary": "File the F29 for a period (async job)",
        "description": "There is no SII webservice for filing an F29. Facturia automates the SII \"Upload\" flow —\na certificate-authenticated session submitting a fixed-format .txt — and models it as a job.\n`confirm_total_payable` must equal the proposal's figure.\n",
        "security": [
          {
            "bearerAuth": [
              "tax:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "period",
                  "confirm_total_payable"
                ],
                "properties": {
                  "period": {
                    "$ref": "#/components/schemas/Period"
                  },
                  "proposal_id": {
                    "type": "string"
                  },
                  "confirm_total_payable": {
                    "type": "integer",
                    "description": "Guard against filing the wrong number. Must match the proposal."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Filing accepted and queued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/F29Filing"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/f29/filings/{filing_id}": {
      "parameters": [
        {
          "name": "filing_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "examples": [
              "f29fil_01K2RD9K3M8T"
            ]
          }
        }
      ],
      "get": {
        "tags": [
          "Tax"
        ],
        "operationId": "getF29Filing",
        "summary": "Retrieve a filing",
        "description": "One F29 filing: the period, the filed totals, and the SII's own acknowledgment if one was captured.",
        "security": [
          {
            "bearerAuth": [
              "tax:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "The filing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/F29Filing"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/expenses": {
      "get": {
        "tags": [
          "Tax"
        ],
        "operationId": "listExpenses",
        "summary": "List ingested foreign-currency charges",
        "description": "Expense-ledger entries imported from a connected bank or accounting feed, filterable by period.",
        "security": [
          {
            "bearerAuth": [
              "tax:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Period"
          }
        ],
        "responses": {
          "200": {
            "description": "A list of expenses.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Expense"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "post": {
        "tags": [
          "Tax"
        ],
        "operationId": "createExpense",
        "summary": "Ingest a charge that may require a tipo-46 factura de compra",
        "description": "Import expense-ledger entries directly (outside a `connections` sync), for a source the empresa does not have an automatic feed for yet.",
        "security": [
          {
            "bearerAuth": [
              "tax:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExpenseCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The stored expense; a suggestion may follow asynchronously.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Expense"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/connections": {
      "get": {
        "tags": [
          "Connectors"
        ],
        "operationId": "listConnections",
        "summary": "List connector connections for this empresa",
        "description": "Connectors (bank feeds, accounting integrations) linked to the empresa, with each one's last sync and status.",
        "security": [
          {
            "bearerAuth": [
              "tax:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A list of connections.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Connection"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "post": {
        "tags": [
          "Connectors"
        ],
        "operationId": "createConnection",
        "summary": "Create a Fintoc bank/card connection",
        "description": "Returns `status: pending_link` with a `link_url`. Send the user through it once; from then\non each sync keeps the foreign-currency movements, converts them into `expense` objects\n(`source: \"fintoc\"`, `fx_source: \"card_rate\"`) and lets the tipo-46 detector run.\nCredentials stay with Fintoc — Facturia stores a link token, never a bank password.\n`connection.linked` fires when the link completes.\n\nIN TEST MODE there is no link to complete, so the connection is born `active`, `link_url`\nis `null` and `connection.linked` fires with the `201` — §4.1's \"complete in-process\nsimulation\", and the only way the connector surface is exercisable before a real bank is\ninvolved.\n",
        "security": [
          {
            "bearerAuth": [
              "tax:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "provider"
                ],
                "properties": {
                  "provider": {
                    "type": "string",
                    "enum": [
                      "fintoc"
                    ]
                  },
                  "account_types": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "checking",
                        "credit_card"
                      ]
                    }
                  },
                  "sync_frequency": {
                    "type": "string",
                    "enum": [
                      "daily",
                      "weekly",
                      "manual"
                    ],
                    "default": "daily"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The connection, awaiting the link flow.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Connection"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/connections/{connection_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConnectionId"
        }
      ],
      "get": {
        "tags": [
          "Connectors"
        ],
        "operationId": "getConnection",
        "summary": "Retrieve a connection",
        "description": "One connector's configuration and sync history, without its stored credentials.",
        "security": [
          {
            "bearerAuth": [
              "tax:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "The connection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Connection"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "delete": {
        "tags": [
          "Connectors"
        ],
        "operationId": "deleteConnection",
        "summary": "Disconnect (revokes the link, stops ingestion)",
        "description": "Expenses already created from this connection are kept.",
        "security": [
          {
            "bearerAuth": [
              "tax:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "responses": {
          "204": {
            "description": "Disconnected."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/connections/{connection_id}/sync": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConnectionId"
        }
      ],
      "post": {
        "tags": [
          "Connectors"
        ],
        "operationId": "syncConnection",
        "summary": "Force a movement sync (async)",
        "description": "`422 connection_not_linked` on a connection that is not `active`: `tax_bank_sync` only\nlooks at linked rows, so queueing one would be a job that runs, does nothing, and reports\n`succeeded`.\n\nIn TEST MODE the job comes back already `succeeded` — there is no bank to call and no\nworker to wait for, so a `queued` job would never move (§4.1).\n",
        "security": [
          {
            "bearerAuth": [
              "tax:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Sync job accepted — `queued` in live, already `succeeded` in test.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/statements": {
      "get": {
        "tags": [
          "Connectors"
        ],
        "operationId": "listStatements",
        "summary": "List uploaded cartolas",
        "description": "Bank or card statements imported for the empresa, the raw material `tax:write` imports feed into the expense ledger and its suggestions.",
        "security": [
          {
            "bearerAuth": [
              "tax:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A list of statement imports.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Statement"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "post": {
        "tags": [
          "Connectors"
        ],
        "operationId": "uploadStatement",
        "summary": "Upload a cartola (CSV/XLSX) to create expenses",
        "description": "Asynchronous. The returned job's `result` carries `rows_read`, `expenses_created`,\n`duplicates_skipped` and per-row `errors[]`. Deduplication is by\n(empresa, date, amount_original, currency, provider) plus `external_id` where present,\nso re-uploading an overlapping cartola is safe.\n\n`202` when this upload created the import; `200` with the ORIGINAL job when the exact same\nBYTES were uploaded before for this empresa (a `sha256` taken before anything touches\nstorage) — no second storage object, no second parse. Same body either way.\n",
        "security": [
          {
            "bearerAuth": [
              "tax:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "format": {
                    "type": "string",
                    "enum": [
                      "auto",
                      "generic_csv",
                      "santander",
                      "bci",
                      "banco_chile",
                      "itau",
                      "scotiabank",
                      "xlsx"
                    ],
                    "default": "auto"
                  },
                  "default_currency": {
                    "type": "string",
                    "description": "Applied to rows whose currency column is absent."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "These exact bytes were already uploaded. The ORIGINAL import job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatementImportJob"
                }
              }
            }
          },
          "202": {
            "description": "Import job accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatementImportJob"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/suggestions": {
      "get": {
        "tags": [
          "Tax"
        ],
        "operationId": "listSuggestions",
        "summary": "List tax suggestions",
        "description": "Categorization suggestions Facturia derived from imported statements and expenses, awaiting a `confirm` or `dismiss`.",
        "security": [
          {
            "bearerAuth": [
              "tax:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/SuggestionType"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "confirmed",
                "auto_confirmed",
                "dismissed",
                "expired"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/Period"
          }
        ],
        "responses": {
          "200": {
            "description": "A list of suggestions.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Suggestion"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/suggestions/{suggestion_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SuggestionId"
        }
      ],
      "get": {
        "tags": [
          "Tax"
        ],
        "operationId": "getSuggestion",
        "summary": "Retrieve a suggestion",
        "description": "One suggestion: what it proposes, and the evidence (matching entries, prior confirmations) it was derived from.",
        "security": [
          {
            "bearerAuth": [
              "tax:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "The suggestion.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Suggestion"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/suggestions/{suggestion_id}/confirm": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SuggestionId"
        }
      ],
      "post": {
        "tags": [
          "Tax"
        ],
        "operationId": "confirmSuggestion",
        "summary": "Act on a suggestion (for `emit_46`, emits the document)",
        "description": "Accept a suggestion, applying its proposed categorization to the entries it names.",
        "security": [
          {
            "bearerAuth": [
              "tax:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "description": "The suggestion, with the resulting document when one was emitted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "suggestion": {
                      "$ref": "#/components/schemas/Suggestion"
                    },
                    "document": {
                      "$ref": "#/components/schemas/Document"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/tax/suggestions/{suggestion_id}/dismiss": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SuggestionId"
        }
      ],
      "post": {
        "tags": [
          "Tax"
        ],
        "operationId": "dismissSuggestion",
        "summary": "Dismiss a suggestion",
        "description": "Reject a suggestion without applying it. Facturia does not re-suggest the identical categorization for the same entries.",
        "security": [
          {
            "bearerAuth": [
              "tax:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "enum": [
                      "not_digital_service",
                      "already_declared",
                      "not_applicable",
                      "other"
                    ]
                  },
                  "note": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The dismissed suggestion.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Suggestion"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/webhook_endpoints": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhookEndpoints",
        "summary": "List webhook endpoints",
        "description": "Webhook endpoints configured for the account, with their `enabled_events` and delivery health, secrets never included.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A list of endpoints.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/WebhookEndpoint"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "createWebhookEndpoint",
        "summary": "Register a webhook endpoint",
        "description": "The `secret` is returned once, on creation, and never again.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "description": {
                    "type": "string"
                  },
                  "enabled_events": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Event types, or `[\"*\"]` for everything including future types.",
                    "default": [
                      "*"
                    ]
                  },
                  "empresas": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Rut"
                    },
                    "description": "Restrict deliveries to these empresas. Defaults to all the key can see."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created endpoint, including its signing secret.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointWithSecret"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/webhook_endpoints/{webhook_endpoint_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookEndpointId"
        }
      ],
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "getWebhookEndpoint",
        "summary": "Retrieve a webhook endpoint",
        "description": "One webhook endpoint's configuration and delivery health, without its signing secret.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "patch": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "updateWebhookEndpoint",
        "summary": "Update a webhook endpoint",
        "description": "Update `url`, `enabled_events` or `description`. The signing secret is unaffected — use `rotate_secret` to change it.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "description": {
                    "type": "string"
                  },
                  "enabled_events": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "empresas": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Rut"
                    }
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "enabled",
                      "disabled"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "deleteWebhookEndpoint",
        "summary": "Delete a webhook endpoint",
        "description": "Delete the endpoint and destroy its signing secret (current and previous) in the vault; both are removed together so no orphaned secret survives the row.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/webhook_endpoints/{webhook_endpoint_id}/ping": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookEndpointId"
        }
      ],
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "pingWebhookEndpoint",
        "summary": "Send a synthetic event to verify plumbing",
        "description": "Send a synthetic `ping` event to the endpoint's current URL, so an integrator can verify signature verification before relying on a real event.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:write"
            ]
          }
        ],
        "responses": {
          "202": {
            "description": "The delivery attempt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDelivery"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/webhook_endpoints/{webhook_endpoint_id}/rotate_secret": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookEndpointId"
        }
      ],
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "rotateWebhookSecret",
        "summary": "Rotate the signing secret",
        "description": "The previous secret keeps signing for 24 hours so you can deploy without downtime.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The endpoint with its new secret.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointWithSecret"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/webhook_endpoints/{webhook_endpoint_id}/deliveries": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookEndpointId"
        }
      ],
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhookDeliveries",
        "summary": "Inspect delivery attempts",
        "description": "Delivery attempts for this endpoint, newest first, with each attempt's response status and whether it will be retried.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "succeeded",
                "failed"
              ]
            }
          },
          {
            "name": "event_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of deliveries.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/WebhookDelivery"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/webhook_endpoints/{webhook_endpoint_id}/deliveries/{delivery_id}/retry": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookEndpointId"
        },
        {
          "name": "delivery_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "examples": [
              "whd_01K2RG1P4M7X01"
            ]
          }
        }
      ],
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "retryWebhookDelivery",
        "summary": "Redeliver an event",
        "description": "Rate-limited at 20/min **per account**, not per credential (ticket #152) — a leaked key\nwould otherwise turn this into a free outbound-request amplifier from Facturia's own IPs.\nThere is also a hard ceiling of 20 total delivery attempts per `(endpoint, event)` pair,\nanswered as `409 too_many_replays`.\n",
        "security": [
          {
            "bearerAuth": [
              "webhooks:write"
            ]
          }
        ],
        "responses": {
          "202": {
            "description": "The new delivery attempt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDelivery"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/events": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listEvents",
        "summary": "Query the event ledger (retained 12 months)",
        "description": "Retention is 12 months — deliberately longer than a typical webhook ledger, so a whole F29\ntax cycle stays replayable. Webhook delivery is at-least-once and unordered; deduplicate\non `id`.\n\n**Polling instead of webhooks.** If you cannot expose an HTTPS endpoint — a personal agent,\na laptop, anything behind NAT — this endpoint is the supported substitute, and `?after=`\nis the parameter that makes it work:\n\n    GET /v1/events?after=evt_01K2RG1P4M7X&limit=100\n\n`after` pages FORWARDS: oldest first, strictly newer than the id you name, `next_cursor`\nadvancing away from it. The loop is `after = next_cursor ?? after`, processing `data` in\norder; it never re-reads a row and needs no set of seen ids. `after` accepts both an\n`evt_…` id (the last event you processed) and a `cur_…` value from `next_cursor`, so your\nstored checkpoint and the token you are handed are interchangeable. When `has_more` is\n`false`, `next_cursor` is `null` — keep the `id` of the last event you processed and send\nthat next time.\n\n`cursor` is the opposite direction and the default: newest first, \"older than this\", which\nis what a dashboard reading a ledger wants. Sending both is `400` rather than a silent\nprecedence — they name positions in opposite orders, and honouring either would skip rows\nfor half the callers with nothing to tell them so.\n",
        "security": [
          {
            "bearerAuth": [
              "webhooks:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "after",
            "in": "query",
            "description": "Forward checkpoint: return events strictly NEWER than this one, oldest first. An\n`evt_…` id or a `cur_…` value from a previous forward page. Mutually exclusive with\n`cursor`.\n",
            "schema": {
              "type": "string",
              "examples": [
                "evt_01K2RG1P4M7X"
              ]
            }
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string",
              "examples": [
                "document.accepted"
              ]
            }
          },
          {
            "name": "empresa_rut",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/Rut"
            }
          },
          {
            "name": "created_at[gte]",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "created_at[lte]",
            "in": "query",
            "description": "Upper bound, inclusive. Honoured since the endpoint shipped and undeclared until now,\nwhich made \"give me one closed month of the ledger\" look impossible from the contract.\n",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of events.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Event"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/events/{event_id}": {
      "parameters": [
        {
          "name": "event_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "examples": [
              "evt_01K2RG1P4M7X"
            ]
          }
        }
      ],
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "getEvent",
        "summary": "Retrieve an event",
        "description": "One event from the account's own ledger — the same object a webhook delivery carries — for a caller reconciling by id rather than by subscription.",
        "security": [
          {
            "bearerAuth": [
              "webhooks:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The event.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/usage": {
      "get": {
        "tags": [
          "Usage"
        ],
        "operationId": "getUsage",
        "summary": "Usage summary for a billing period",
        "description": "Current-period request and emission counts against the account's plan limits.",
        "security": [
          {
            "bearerAuth": [
              "usage:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Period"
          },
          {
            "name": "group_by",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "empresa",
                "tipo_dte",
                "metric"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The usage summary.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageSummary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/usage/records": {
      "get": {
        "tags": [
          "Usage"
        ],
        "operationId": "listUsageRecords",
        "summary": "The immutable usage ledger",
        "description": "One record per accepted document. A document is recorded when the SII accepts it —\nrejected, failed and sandbox documents never appear here. Corrections are compensating\nrecords with negative quantity, never updates. This is a report, not a meter: the plan\nis flat and unlimited, so nothing here is priced per document.\n",
        "security": [
          {
            "bearerAuth": [
              "usage:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Period"
          },
          {
            "name": "metric",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/UsageMetric"
            }
          },
          {
            "name": "empresa_rut",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/Rut"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of usage records.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/UsageRecord"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/jobs/{job_id}": {
      "parameters": [
        {
          "name": "job_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "examples": [
              "job_01K2R8B4V6WQ2X"
            ]
          }
        }
      ],
      "get": {
        "tags": [
          "Meta"
        ],
        "operationId": "getJob",
        "summary": "Retrieve an async job (folio request, send, cesión, RCV sync, import)",
        "description": "One endpoint for every asynchronous operation the API starts. Read `type` first: it decides\nthe shape of `result` and which scope may read the job (the scope that STARTS that kind of\njob, plus the read scope for the subject it acts on — a `documents:read` key may watch a\nsend, and is refused a tax filing).\n\n`Facturia-Poll-After` says when to ask again, in seconds, and is ABSENT once `status` is\n`succeeded` or `failed`: there is nothing left to observe. Do not poll faster than it says —\nnothing on this surface can finish inside the queue's own 10-second claim interval.\n\n`mode` is on the object, and a job created in the other mode does not exist for this\ncredential: the answer is `404`, not `403` (§4.2).\n",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The job.",
            "headers": {
              "Facturia-Poll-After": {
                "description": "Seconds until the next useful poll. Absent on a terminal job. The cadence is per\n`type`: 10 s for `document_send` and `statement_import`, 15 s for `connection_sync`,\n30 s for `rcv_sync` and `cesion_submit`, 60 s for `folio_request` — the SII's\ntimbraje is a scripted portal walk and asking sooner only earns a `429`.\n",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                },
                "examples": {
                  "default": {
                    "summary": "A folio_request job, succeeded.",
                    "value": {
                      "id": "job_01K2R8B4V6WQ2X",
                      "object": "job",
                      "type": "folio_request",
                      "status": "succeeded",
                      "mode": "live",
                      "empresa_id": "emp_01K2R6ZC4P8QW1VN7T3MHDY9EK",
                      "tipo_dte": 33,
                      "quantity": 50,
                      "result": {
                        "tipo_dte": 33,
                        "quantity": 50,
                        "caf_id": "caf_01K2P4V8QW2M",
                        "from": 1001,
                        "to": 1050,
                        "note": "Rango entregado por el SII."
                      },
                      "created_at": "2026-09-01T14:02:11Z",
                      "completed_at": "2026-09-01T14:02:47Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`empresa_header_forbidden` — this operation is account-level (a job belongs to the\nkey's account, not to one empresa); a `Facturia-Empresa` header on it is refused.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/mandato_terms": {
      "get": {
        "tags": [
          "Meta"
        ],
        "operationId": "getMandatoTerms",
        "summary": "The current mandato text and version",
        "description": "The text §7.3 tells you to present before posting an acceptance. Unauthenticated, for the\nsame reason `GET /error_codes` is: the body is a constant with no tenant data in it.\n\nREAD `status` FIRST. `draft` means the text has not finished attorney review and is not yet\nbinding — do not present it to a representante legal as terms they are signing. It is\nderived from the document's own review banner, so it flips to `active` on the day the\nreview lands rather than on the day someone remembers to update a constant.\n\nThis endpoint hands over the words only. Recording an acceptance is\n`POST /empresas/{empresa_id}/mandato`.\n\n`annexes[]` carries the documents the contract's own header calls «parte integrante» —\nprices, personal data, the automation matrix — with their Markdown, because a page that\nasks somebody to accept a contract has to be able to show them all of it (ticket #519).\n`content_hash` still covers `text` alone: those are the bytes an acceptance records.\n\n`?version=` fetches a specific version. Omit it for the text in force — a caller who\nforgets it must never be handed a draft. Use it to read a draft of what comes next before\nit binds anyone, and to fetch the exact version an older acceptance recorded (the mandato\nreceipt's `terms_url` does precisely that). An unknown value is `400 invalid_field`.\n",
        "security": [],
        "parameters": [
          {
            "name": "version",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "A version from `available_versions`. Omitted means the current text.\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Mandato terms.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "version",
                    "status",
                    "effective_date",
                    "content_hash",
                    "text"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "mandato_terms"
                    },
                    "version": {
                      "type": "string",
                      "examples": [
                        "0.1"
                      ]
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "draft",
                        "active"
                      ],
                      "description": "`draft` until attorney review completes. An integrator should refuse to collect an acceptance against a draft."
                    },
                    "effective_date": {
                      "type": "string",
                      "format": "date",
                      "examples": [
                        "2026-08-14"
                      ]
                    },
                    "language": {
                      "type": "string",
                      "examples": [
                        "es-CL"
                      ]
                    },
                    "content_type": {
                      "type": "string",
                      "examples": [
                        "text/markdown"
                      ]
                    },
                    "content_hash": {
                      "type": "string",
                      "description": "`sha256:…` over exactly the bytes of `text`. Pin it if you cache the terms — it is how you tell that the version you showed someone is still the one we serve.",
                      "examples": [
                        "sha256:6447ef0de8961…"
                      ]
                    },
                    "text": {
                      "type": "string",
                      "description": "The mandato, in Markdown."
                    },
                    "current": {
                      "type": "boolean",
                      "description": "Whether this is the version an acceptance recorded right now would be recorded against. `false` for a draft of what comes next, which `status: draft` alone could not distinguish from «in force, still under review»."
                    },
                    "available_versions": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Every version this build can serve through `?version=`."
                    },
                    "annexes": {
                      "type": "array",
                      "description": "The annexes this version declares «parte integrante» of the contract, in the order its header lists them.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "examples": [
                              "anexo-a"
                            ]
                          },
                          "title": {
                            "type": "string",
                            "examples": [
                              "Anexo A — Plan, Precios y Condiciones Comerciales"
                            ]
                          },
                          "file": {
                            "type": "string",
                            "examples": [
                              "anexo-a-precios.md"
                            ]
                          },
                          "markdown": {
                            "type": "string",
                            "description": "The whole annex, in Markdown."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/error_codes": {
      "get": {
        "tags": [
          "Meta"
        ],
        "operationId": "listErrorCodes",
        "summary": "The machine-readable error dictionary (unauthenticated)",
        "description": "Every code this API can put in an `error.code`, plus the SII rejection codes that appear\ninside a document's `rejection_reasons[]`. Told apart by `type`: a `rejection` row is not a\nresponse and carries `http_status: null`.\n\n`retriable` is answered about the IDENTICAL request — `rate_limit_exceeded` is retriable\nbecause waiting fixes it, `totals_mismatch` is not. Build retry policy on that field rather\nthan on the status code.\n\nNo credential, and no rate limit: the body is a constant with no tenant data in it.\n",
        "security": [],
        "responses": {
          "200": {
            "description": "Every Facturia error code, its SII counterpart, and whether it is retriable.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/List"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ErrorCode"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/test_helpers/received_documents": {
      "post": {
        "tags": [
          "Test helpers"
        ],
        "operationId": "injectReceivedDocument",
        "summary": "Inject a supplier DTE as if it had arrived at the reception address",
        "description": "Sandbox only: inject a supplier DTE as if it had just arrived at the reception address, without waiting for a real counterparty to send one.",
        "security": [
          {
            "bearerAuth": [
              "received:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "tipo_dte",
                  "emisor"
                ],
                "properties": {
                  "tipo_dte": {
                    "$ref": "#/components/schemas/TipoDte"
                  },
                  "folio": {
                    "type": "integer"
                  },
                  "emisor": {
                    "$ref": "#/components/schemas/Party"
                  },
                  "issue_date": {
                    "type": "string",
                    "format": "date"
                  },
                  "totals": {
                    "$ref": "#/components/schemas/Totals"
                  },
                  "items": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/DocumentItem"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The injected received document; `received_document.created` fires.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReceivedDocument"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/test_helpers/rcv/seed": {
      "post": {
        "tags": [
          "Test helpers"
        ],
        "operationId": "seedRcv",
        "summary": "Seed RCV rows for a period so F29 propuestas have data",
        "description": "Sandbox only: seed the purchases/sales ledgers with synthetic entries, so RCV-dependent flows (characterization, F29) can be tested without a real SII sync.",
        "security": [
          {
            "bearerAuth": [
              "tax:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "period"
                ],
                "properties": {
                  "period": {
                    "$ref": "#/components/schemas/Period"
                  },
                  "purchases": {
                    "type": "integer",
                    "default": 10
                  },
                  "sales": {
                    "type": "integer",
                    "default": 10
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The rows were written, in this request. No job, and nothing to poll: the sandbox has no\nworker to wait for (§4.1), so a seed that answered `202` with a job would be a promise\nof an asynchrony that does not exist here.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RcvSeed"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/test_helpers/clock": {
      "post": {
        "tags": [
          "Test helpers"
        ],
        "operationId": "advanceClock",
        "summary": "Advance this empresa's simulated clock",
        "description": "Exercises the 8-day acuse window, folio expiry and F29 deadlines without waiting.",
        "security": [
          {
            "bearerAuth": [
              "empresas:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "advance_days": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 400
                  },
                  "set_to": {
                    "type": "string",
                    "format": "date"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The empresa's new simulated date.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "test_clock"
                    },
                    "empresa_rut": {
                      "$ref": "#/components/schemas/Rut"
                    },
                    "now": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/test_helpers/empresas/{empresa_id}/onboarding": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        }
      ],
      "post": {
        "tags": [
          "Test helpers"
        ],
        "operationId": "setOnboardingState",
        "summary": "Drive an empresa's onboarding to any state",
        "description": "Sandbox only: force the empresa's onboarding spine to a named `onboarding_status`, skipping every step between here and there.",
        "security": [
          {
            "bearerAuth": [
              "empresas:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "status"
                ],
                "properties": {
                  "status": {
                    "$ref": "#/components/schemas/OnboardingStatus"
                  },
                  "step": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The forced onboarding state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Onboarding"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/test_helpers/empresas/{empresa_id}/onboarding/steps": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EmpresaId"
        }
      ],
      "post": {
        "tags": [
          "Test helpers"
        ],
        "operationId": "setOnboardingStep",
        "summary": "Force one onboarding step to a status",
        "description": "The finer of the two onboarding levers. `POST .../onboarding` moves the empresa's\nSTATE; this moves ONE STEP, which is how you produce the parts of §7.4 a state\ncannot express: a step at `action_required` with its `action_url`, a certification\ngate that `failed` for one `tipo_dte` while the others passed, a step `skipped`\nbecause the RUT was already an authorized emisor.\n\nThe four certification steps (`set_de_pruebas`, `simulacion`, `intercambio`,\n`muestras_impresas`) exist per document type. Omit `tipo_dte` to force every\ndeclared type at once; supply it to force one.\n\nReturns the full onboarding object and fires the same `empresa.onboarding.*`\nwebhook the real machine would.\n",
        "security": [
          {
            "bearerAuth": [
              "empresas:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "step",
                  "status"
                ],
                "properties": {
                  "step": {
                    "type": "string",
                    "enum": [
                      "mandato",
                      "certificate",
                      "sii_postulacion",
                      "set_de_pruebas",
                      "simulacion",
                      "intercambio",
                      "muestras_impresas",
                      "cumplimiento",
                      "resolucion",
                      "habilitacion",
                      "folios"
                    ]
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "pending",
                      "in_progress",
                      "action_required",
                      "completed",
                      "failed",
                      "skipped"
                    ]
                  },
                  "tipo_dte": {
                    "$ref": "#/components/schemas/TipoDte",
                    "description": "Only meaningful on a certification step. Must be one of the empresa's\ndeclared `doc_types`.\n"
                  },
                  "blocked_reason": {
                    "type": "object",
                    "description": "Structured reason to attach to a `failed` or `action_required` step."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The onboarding object after the forced step.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Onboarding"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/test_helpers/documents/{document_id}/estado": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "post": {
        "tags": [
          "Test helpers"
        ],
        "operationId": "forceDocumentEstado",
        "summary": "Force the raw SII estado of an already-emitted sandbox document",
        "description": "`test.outcome` on `POST /documents` forces an outcome AT EMISSION. This forces one\nAFTERWARDS, and it is the only deterministic way to exercise the transition a webhook\nconsumer actually cares about: without it, `sent → accepted` means racing the sandbox's own\n~1 s settle, and `stuck → accepted` is unreachable.\n\nIT WRITES THE ESTADO, NOT THE STATUS, on purpose. The public `status` is then derived by\nthe ordinary mapping, so a test that drives a document to `DNK` proves the `reparo` mapping\nworks rather than asserting it. The same `document.*` lifecycle event the real transition\nwould fire is published, so a consumer under test receives exactly what production sends.\n\nSandbox only, checked three times: the key's mode, the empresa's flag, and the DOCUMENT's\nown `is_sandbox` — which is stamped at insert and immutable, so a real document stays real\neven if the empresa flag were flipped afterwards.\n",
        "security": [
          {
            "bearerAuth": [
              "documents:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "estado"
                ],
                "properties": {
                  "estado": {
                    "type": "string",
                    "description": "The SII estado to write, exactly as the poll executor would.",
                    "enum": [
                      "DOK",
                      "DNK",
                      "FAU",
                      "FNA",
                      "FAN",
                      "EMP",
                      "RCT",
                      "EPR",
                      "SOK",
                      "RPR",
                      "RLV"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The document, with its derived status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/test_helpers/f29/proposals": {
      "post": {
        "tags": [
          "Test helpers"
        ],
        "operationId": "seedF29Proposal",
        "summary": "Inject a computed F29 propuesta",
        "description": "Computing a propuesta is a WORKER job, and the worker is not part of the sandbox's promise\n(§4.1: test mode is \"a complete in-process simulation\"). Without this helper the sandbox\ncan seed an RCV and then wait for ever for a propuesta only a running worker produces,\nwhich makes the whole §12.3/§12.4 surface untestable in test mode.\n\nIt writes the same row the real computation writes, so everything downstream — the\nserializer, the upload-file link, and above all the FILING GATE — reads an ordinary\npropuesta. The gate is not bypassed: it still reads `total_payable` and `observations` from\nthis row, and it still demands a validation (see `/test_helpers/f29/validations`).\n\nRe-seeding the same period updates it rather than creating a second one.\n",
        "security": [
          {
            "bearerAuth": [
              "tax:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "period"
                ],
                "properties": {
                  "period": {
                    "$ref": "#/components/schemas/Period"
                  },
                  "summary": {
                    "type": "object",
                    "description": "Any omitted field defaults to 0, and `total_payable` to\n`max(0, iva_debito - iva_credito) + iva_retenido + ppm`.\n",
                    "properties": {
                      "iva_debito": {
                        "type": "integer"
                      },
                      "iva_credito": {
                        "type": "integer"
                      },
                      "iva_retenido": {
                        "type": "integer"
                      },
                      "ppm": {
                        "type": "integer"
                      },
                      "total_payable": {
                        "type": "integer"
                      }
                    }
                  },
                  "codes": {
                    "type": "array",
                    "description": "The F29 code rows. Omit for a plausible default set.",
                    "items": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "integer"
                        },
                        "label": {
                          "type": "string"
                        },
                        "value": {
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "observations": {
                    "type": "array",
                    "description": "An observation with `severity: error` lands the propuesta `review_required`,\nwhich is what the filing gate refuses.\n",
                    "items": {
                      "type": "object",
                      "properties": {
                        "severity": {
                          "type": "string",
                          "enum": [
                            "info",
                            "warning",
                            "error"
                          ]
                        },
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The seeded propuesta.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "f29_proposal"
                    },
                    "id": {
                      "type": "string"
                    },
                    "period": {
                      "$ref": "#/components/schemas/Period"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "ready",
                        "blocked"
                      ]
                    },
                    "summary": {
                      "type": "object",
                      "properties": {
                        "total_payable": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    },
    "/test_helpers/f29/validations": {
      "post": {
        "tags": [
          "Test helpers"
        ],
        "operationId": "seedF29Validation",
        "summary": "Force the cross-validation verdict for a period",
        "description": "The real cross-validation SKIPS the SII fetch for a sandbox empresa and always records\n`unavailable` — correctly, because a sandbox empresa has no SII to contrast against. But\n`unavailable` is not `validated`, so without this helper the filing gate refuses EVERY\nsandbox filing with `not_validated` and the rest of §12.4 can never be reached.\n\nForcing the VERDICT rather than the filing is what keeps it honest: a test that drives a\nproposal to `validated` and then files proves the gate lets a validated proposal through,\nand one that leaves it `unavailable` proves the gate stops one that is not. The gate itself\nis untouched either way.\n",
        "security": [
          {
            "bearerAuth": [
              "tax:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EmpresaHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "period"
                ],
                "properties": {
                  "period": {
                    "$ref": "#/components/schemas/Period"
                  },
                  "verdict": {
                    "type": "string",
                    "enum": [
                      "validated",
                      "discrepant",
                      "unavailable"
                    ],
                    "default": "validated"
                  },
                  "material_count": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Only on `discrepant`; how many material differences to record."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The recorded validation.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "f29_validation"
                    },
                    "period": {
                      "$ref": "#/components/schemas/Period"
                    },
                    "verdict": {
                      "type": "string",
                      "enum": [
                        "validated",
                        "discrepant",
                        "unavailable"
                      ]
                    },
                    "source": {
                      "type": "string",
                      "enum": [
                        "sii_propuesta",
                        "none"
                      ]
                    },
                    "material_count": {
                      "type": "integer"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Default"
          }
        }
      }
    }
  },
  "webhooks": {
    "document.lifecycle": {
      "post": {
        "operationId": "onDocumentLifecycle",
        "summary": "document.created | .sent | .accepted | .reparo | .rejected | .failed | .annulled",
        "description": "Fired on every transition of an emitted document. `document.accepted` (SII estado DOK)\nis the one most integrations act on, and the one that produces a usage record.\n\n`queued` earns no event on purpose: `document.created` already said the document exists,\nand every pre-transmission step reads as `queued` from outside.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/SignatureHeader"
          },
          {
            "$ref": "#/components/parameters/EventIdHeader"
          },
          {
            "$ref": "#/components/parameters/DeliveryIdHeader"
          },
          {
            "$ref": "#/components/parameters/DeliveryAttemptHeader"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "document.created",
                          "document.sent",
                          "document.accepted",
                          "document.reparo",
                          "document.rejected",
                          "document.failed",
                          "document.annulled"
                        ]
                      },
                      "data": {
                        "type": "object",
                        "properties": {
                          "object": {
                            "$ref": "#/components/schemas/Document"
                          }
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge within 10 seconds. Anything else is a failed delivery and is retried."
          }
        }
      }
    },
    "document.delivery": {
      "post": {
        "operationId": "onDocumentDelivery",
        "summary": "document.emailed | .email_failed",
        "description": "The document EMAIL, which is a different state machine from the SII round trip and is kept\napart in the vocabulary for that reason. `document.email_failed` fires on PERMANENT failure\nonly — a delivery the job will retry is not news, and a webhook per attempt is noise nobody\ncan act on.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/SignatureHeader"
          },
          {
            "$ref": "#/components/parameters/EventIdHeader"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "document.emailed",
                          "document.email_failed"
                        ]
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged."
          }
        }
      }
    },
    "document.cesion": {
      "post": {
        "operationId": "onDocumentCesion",
        "summary": "document.cesion_submitted | .cesion_confirmed | .cesion_failed",
        "description": "Ley 19.983 cesión (ticket #60, corrected by #69). **Settle money on\n`document.cesion_confirmed` and on nothing else.** `document.cesion_submitted` means «AEC\ngenerado, registro pendiente» — the portal proved the AEC FILE exists, which is not\nregistration in the Registro de Cesión de Créditos. `document.cesion_failed` covers a\nrefusal, a fence, an unrecognised page, and a registration that could never be confirmed.\n\nNot an overload of `document.failed`, which means the SII rejected the EMISSION: a\nsubscriber acting on that name must never be told an unrelated act went wrong.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/SignatureHeader"
          },
          {
            "$ref": "#/components/parameters/EventIdHeader"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "document.cesion_submitted",
                          "document.cesion_confirmed",
                          "document.cesion_failed"
                        ]
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged."
          }
        }
      }
    },
    "received_document.lifecycle": {
      "post": {
        "operationId": "onReceivedDocumentLifecycle",
        "summary": "received_document.created | .deadline_approaching | .accepted | .rejected | .receipt_acknowledged",
        "parameters": [
          {
            "$ref": "#/components/parameters/SignatureHeader"
          },
          {
            "$ref": "#/components/parameters/EventIdHeader"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "received_document.created",
                          "received_document.deadline_approaching",
                          "received_document.accepted",
                          "received_document.rejected",
                          "received_document.receipt_acknowledged"
                        ]
                      },
                      "data": {
                        "type": "object",
                        "properties": {
                          "object": {
                            "$ref": "#/components/schemas/ReceivedDocument"
                          }
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged."
          }
        }
      }
    },
    "reception.operations": {
      "post": {
        "operationId": "onReceptionOperations",
        "summary": "reception.acuse_sent | .acuse_failed | .quarantined",
        "description": "The Intercambio side of reception, named `reception.*` rather than `received_document.*`\nprecisely so nobody can subscribe to \"the supplier's invoice was acknowledged\" believing\nthey subscribed to acceptance. The acuse is the RespuestaEnvio — the technical\nacknowledgement the standard requires — and the SII's own words are that it «no implica la\naceptación comercial». Every acuse payload carries `is_commercial_acceptance: false`.\n\n`reception.quarantined` is mail that reached the reception address and could not be routed\nto any empresa. Operational: there is no document to point at, which is the problem being\nreported.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/SignatureHeader"
          },
          {
            "$ref": "#/components/parameters/EventIdHeader"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "reception.acuse_sent",
                          "reception.acuse_failed",
                          "reception.quarantined"
                        ]
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged."
          }
        }
      }
    },
    "empresa.lifecycle": {
      "post": {
        "operationId": "onEmpresaLifecycle",
        "summary": "empresa.* | certificate.* | folios.* | connection.* | api_key.* | webhook_endpoint.disabled",
        "description": "Everything that happens to an empresa or to the account's own machinery rather than to a\ndocument. `empresa.onboarding.action_required` is the one an agent driving a guided\nonboarding acts on: it carries the same `pending_action` the refusal and\n`GET /empresas/{id}/onboarding` carry. `empresa.onboarding.step.completed` fires once per\nstep that just reached `completed`, with `data.detail.step` naming it — so a subscriber\ndoes not have to diff `steps[]` between two reads to notice one step finishing.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/SignatureHeader"
          },
          {
            "$ref": "#/components/parameters/EventIdHeader"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "empresa.created",
                          "empresa.updated",
                          "empresa.onboarding.updated",
                          "empresa.onboarding.step.completed",
                          "empresa.onboarding.action_required",
                          "empresa.onboarding.completed",
                          "empresa.suspended",
                          "certificate.uploaded",
                          "certificate.revoked",
                          "certificate.expiring",
                          "certificate.expired",
                          "folios.low",
                          "folios.exhausted",
                          "folios.replenished",
                          "webhook_endpoint.disabled",
                          "connection.linked",
                          "connection.sync_succeeded",
                          "connection.sync_failed",
                          "connection.requires_relink",
                          "api_key.created",
                          "api_key.rolled",
                          "api_key.revoked"
                        ]
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged."
          }
        }
      }
    },
    "tax.lifecycle": {
      "post": {
        "operationId": "onTaxLifecycle",
        "summary": "tax.rcv.synced | tax.f29.* | tax.suggestion.* | tax.statement.imported",
        "parameters": [
          {
            "$ref": "#/components/parameters/SignatureHeader"
          },
          {
            "$ref": "#/components/parameters/EventIdHeader"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "tax.rcv.synced",
                          "tax.f29.proposal.ready",
                          "tax.f29.proposal.updated",
                          "tax.f29.review_required",
                          "tax.f29.filing.submitted",
                          "tax.f29.filing.succeeded",
                          "tax.f29.filing.rejected",
                          "tax.f29.filing.failed",
                          "tax.suggestion.created",
                          "tax.suggestion.auto_confirmed",
                          "tax.statement.imported"
                        ]
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged."
          }
        }
      }
    },
    "ping": {
      "post": {
        "operationId": "onPing",
        "summary": "ping",
        "description": "Synthetic, from `POST /webhook_endpoints/{id}/ping`. Never billed and never stored against\nan empresa — it exists so an endpoint can be proven reachable and its signature verified\nbefore a real event depends on it.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/SignatureHeader"
          },
          {
            "$ref": "#/components/parameters/EventIdHeader"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Event"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "ping"
                        ]
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "operatorSecret": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Admin-Secret",
        "description": "The Facturia OPERATOR's secret — never an API key, root keys included. Declared by exactly\none operation, `POST /empresas/{empresa_id}/resolucion/verify`: the per-empresa\nconfirmation of an irreversible production timbraje (sii-api#581 D1). Customers do not\nhold it and cannot call that operation.\n"
      },
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "`Authorization: Bearer sk_live_…` or `sk_test_…`. The prefix decides the mode:\ntest keys drive a fully mocked sandbox that never contacts the SII.\nKeys carry a scope set and an empresa allowlist; a key allowlisted to exactly one\nempresa is an \"empresa token\" and makes the `Facturia-Empresa` header optional.\nRoot keys (`sk_live_root_…` / `sk_test_root_…`) carry only `keys:write` and exist solely\nto mint, roll and revoke other keys — a minted key can never exceed its minter's\nempresas, scopes or mode.\n\nEVERY OPERATION BELOW DECLARES THE SCOPE IT ACTUALLY CHECKS, as an OAuth-style scope on\nthis scheme (`security: [{bearerAuth: [documents:write]}]`), not only in the `Scope` enum.\n`[]` means \"any authenticated key, no particular scope\" (`GET /account`, `GET /api_keys`,\n`GET /jobs/{job_id}` — whose real gate is per job `type`, spelled out in its own\ndescription); two entries under the same operation are alternatives, exactly like\n`requireAnyScope` in the code (`POST …/mandato`, `POST …/onboarding/go_live`) — either\nscope is sufficient, neither is required alongside the other. This is not strict OpenAPI\n3.1: the spec reserves non-empty scope arrays for `oauth2`/`openIdConnect` schemes, and a\npedantic validator may warn on an `http`/`bearer` scheme carrying one. It was chosen over a\nparallel `x-facturia-scopes` extension because every codegen target this contract is known\nto feed (`v1-published-contracts.test.ts`, the docs-site's own renderer) reads `security`\nalready for \"does this need a credential\" and simply ignores scopes it does not model,\nwhile an `x-` extension is invisible to that same tooling unless it is taught to look —\nthe whole reason ADR-0011 exists is that undiscoverable is indistinguishable from absent.\n`test/agent-contract.test.ts` §H asserts each declared scope equals the literal `requireScope`\n(or `requireAnyScope`) call at that route.\n"
      }
    },
    "parameters": {
      "EmpresaHeader": {
        "name": "Facturia-Empresa",
        "in": "header",
        "required": false,
        "description": "Target empresa: canonical RUT (`77928532-4`) or empresa id (`emp_…`).\nRequired on every empresa-scoped call unless the key is an empresa token. This header is\nthe ONLY mechanism: there is no `?empresa=` query parameter and no `empresa` body field.\nOmitted where required → `400 empresa_required`; outside the allowlist → `403 empresa_not_allowed`.\n",
        "schema": {
          "type": "string",
          "examples": [
            "77928532-4"
          ]
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Client-generated key, unique per logical operation, retained 24h and scoped to\n(account, empresa, endpoint). Replays return the original response with\n`Facturia-Idempotent-Replay: true`. Same key + different body → `409`.\n",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 25
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "description": "Opaque cursor from a previous response's `next_cursor`.",
        "schema": {
          "type": "string"
        }
      },
      "Period": {
        "name": "period",
        "in": "query",
        "description": "Tax period, `YYYY-MM`, America/Santiago.",
        "schema": {
          "$ref": "#/components/schemas/Period"
        }
      },
      "PeriodRequired": {
        "name": "period",
        "in": "query",
        "required": true,
        "schema": {
          "$ref": "#/components/schemas/Period"
        }
      },
      "PeriodPath": {
        "name": "period",
        "in": "path",
        "required": true,
        "schema": {
          "$ref": "#/components/schemas/Period"
        }
      },
      "EmpresaId": {
        "name": "empresa_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "examples": [
            "emp_01K2R6ZC4P8QW1VN7T3MHDY9EK"
          ]
        }
      },
      "CounterpartyRut": {
        "name": "counterparty_rut",
        "in": "path",
        "required": true,
        "description": "The counterparty's RUT, canonical (`76000002-7`). It is the key of the relationship and\nof the address book entry — a counterparty has no id of its own, because there is exactly\none of each per RUT.\n",
        "schema": {
          "$ref": "#/components/schemas/Rut"
        }
      },
      "KeyId": {
        "name": "key_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "examples": [
            "key_01K2QWB7T4N9"
          ]
        }
      },
      "ConnectionId": {
        "name": "connection_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "examples": [
            "con_01K2RJ7B2W4M"
          ]
        }
      },
      "DocumentId": {
        "name": "document_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "examples": [
            "doc_01K2R7Q4XW9M3B8ZC5YHTVJD6N"
          ]
        }
      },
      "PreviewId": {
        "name": "preview_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "examples": [
            "prev_01K2R9M3D8V5QW7N"
          ]
        }
      },
      "ReceivedDocumentId": {
        "name": "received_document_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "examples": [
            "rdoc_01K2R9M3D8V5QW7N"
          ]
        }
      },
      "RcvEntryId": {
        "name": "entry_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "examples": [
            "rcv_01K2RB7Q2M4X"
          ]
        }
      },
      "SuggestionId": {
        "name": "suggestion_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "examples": [
            "sug_01K2RE2X7Q9V"
          ]
        }
      },
      "WebhookEndpointId": {
        "name": "webhook_endpoint_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "examples": [
            "whe_01K2RF5N8Q2W"
          ]
        }
      },
      "SignatureHeader": {
        "name": "Facturia-Signature",
        "in": "header",
        "required": true,
        "description": "`t=<unix>,v1=<hex>` — HMAC-SHA256 over `\"{t}.{raw_body}\"` keyed by the endpoint's\n`whsec_` secret. Verify against raw bytes; reject anything older than 300s.\nDuring rotation two `v1=` values appear; accept if any matches.\n",
        "schema": {
          "type": "string"
        }
      },
      "EventIdHeader": {
        "name": "Facturia-Event-Id",
        "in": "header",
        "required": true,
        "description": "Deduplicate on this. Delivery is at-least-once and unordered.",
        "schema": {
          "type": "string"
        }
      },
      "DeliveryIdHeader": {
        "name": "Facturia-Delivery-Id",
        "in": "header",
        "required": true,
        "description": "`whd_…` — this ATTEMPT's id, as opposed to `Facturia-Event-Id`, which is the event's. Two\ndeliveries of the same event share an event id and differ here. It is the id\n`POST /webhook_endpoints/{id}/deliveries/{delivery_id}/retry` takes.\n",
        "schema": {
          "type": "string",
          "examples": [
            "whd_01K2RG1P4M7X01"
          ]
        }
      },
      "DeliveryAttemptHeader": {
        "name": "Facturia-Delivery-Attempt",
        "in": "header",
        "required": false,
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      }
    },
    "responses": {
      "Default": {
        "description": "An error not otherwise enumerated for this operation, in the same §5.5 envelope as every\nother refusal. Branch on `error.code`, never on the HTTP status alone or on `error.message`\n— a code this operation has not been seen to throw yet is still safer to route on than a\nstring meant for a human.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "BadRequest": {
        "description": "Malformed or missing input.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, malformed or revoked key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Valid key, not permitted — scope, empresa allowlist, or mode.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "NotFound": {
        "description": "No such object under this key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Conflict": {
        "description": "State conflict — idempotency reuse, duplicate external_id, artifact not ready.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Unprocessable": {
        "description": "Well-formed but violates a domain rule — folios exhausted, acuse window closed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "`subscription_required` - this live act needs an active subscription for the EMPRESA\nthe request names, and that empresa has none (enforced). The subscription is per\nempresa: another empresa on the same account is unaffected, and `error.details`\ncarries `empresa`, `empresa_rut`, `empresa_name`, `account_plan`,\n`subscription_status`, `grace_until`, `blocked_by`, a `next` handoff and a\n`checkout_url` pointing at that empresa.\nOr `plan_limit_reached` - a plan quota is exhausted (declared, not yet enforced).\nTest mode is never gated on either.\n\n**ELIGIBILITY IS EVALUATED FIRST** (2026-09-04, #475). This refusal now arrives ONLY\nwhen the empresa is otherwise able to emit: while its checklist is unfinished, its\n`puesta en vivo` pending or the empresa suspended, the act is refused by\n`422 empresa_not_emitting` with the checklist handoff instead. So this `402` and\n`Empresa.capabilities.blocked_by` can no longer name two different next actions for one\nstate — which they did, the object saying «sign the mandato» while the door said «pay».\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          },
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            }
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            }
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "Rut": {
        "type": "string",
        "pattern": "^[0-9]{1,8}-[0-9K]$",
        "description": "Canonical RUT — no dots, one hyphen, uppercase K.",
        "examples": [
          "77928532-4",
          "76543212-K"
        ]
      },
      "Period": {
        "type": "string",
        "pattern": "^\\d{4}-\\d{2}$",
        "description": "Tax period `YYYY-MM` (America/Santiago).",
        "examples": [
          "2026-07"
        ]
      },
      "Clp": {
        "type": "integer",
        "description": "Amount in CLP. Integer pesos, never decimals or strings."
      },
      "Mode": {
        "type": "string",
        "enum": [
          "live",
          "test"
        ]
      },
      "TipoDte": {
        "type": "integer",
        "enum": [
          33,
          34,
          39,
          41,
          46,
          52,
          56,
          61
        ],
        "description": "33 factura afecta · 34 factura exenta · 39 boleta afecta · 41 boleta exenta ·\n46 factura de compra · 52 guía de despacho · 56 nota de débito · 61 nota de crédito.\nBoletas (39/41) travel a different SII transport; that is invisible through this API.\n"
      },
      "Address": {
        "type": "object",
        "properties": {
          "street": {
            "type": "string",
            "maxLength": 70
          },
          "comuna": {
            "type": "string",
            "maxLength": 20
          },
          "city": {
            "type": "string",
            "maxLength": 20
          }
        }
      },
      "EmpresaAddress": {
        "type": "object",
        "description": "The address AS READ BACK off an empresa (#496). Structurally `Address`, with every member\nnullable, because `POST /v1/empresas` requires none of the three and stores `null` for\nwhatever was omitted — which is the ordinary create. The REQUEST shape stays `Address`\nwith non-null strings: what you may send and what you may read back are different\nstatements, and collapsing them would either weaken the request or keep lying about the\nresponse. Same family of defect as #473's `contact_email`.\n",
        "properties": {
          "street": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 70
          },
          "comuna": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20
          },
          "city": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20
          }
        }
      },
      "Party": {
        "type": "object",
        "required": [
          "rut"
        ],
        "description": "A counterparty — a company OR a natural person. There is no company flag, and none is\nneeded: a natural person (`rut` + `legal_name`, no `giro`, no `address`) is a valid\nreceptor for a boleta (39/41). Its name is written to `<RznSocRecep>` and to the timbre's\n`<RSR>`, its RUT to `<RUTRecep>`/`<RR>` — the boleta a patient presents to an Isapre or a\nseguro complementario. Without `legal_name` the boleta names the RUT beside «Sin RUT»;\nwithout a receptor it is issued to the anonymous consumer `66666666-6`.\n\nA nota de crédito (61) to a natural person is valid too, but it is a factura-family\ndocument: like every 61 it needs `giro`, `address.street` and `address.comuna`, even when\nit annuls a boleta. `POST /v1/documents/{id}/credit_note` copies the receptor from the\noriginal boleta, so store those three once with\n`PATCH /v1/empresas/{id}/counterparties/{rut}` before annulling, or the note is refused\nwith `400 receptor_incomplete` before any folio is spent.\n",
        "properties": {
          "rut": {
            "$ref": "#/components/schemas/Rut"
          },
          "legal_name": {
            "type": "string",
            "maxLength": 100
          },
          "giro": {
            "type": "string"
          },
          "address": {
            "$ref": "#/components/schemas/Address"
          },
          "contact": {
            "type": "string"
          },
          "foreign": {
            "type": "boolean",
            "description": "Foreign counterparty. Use the SII generic RUT `55555555-5` when unregistered."
          },
          "email_document_to": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "email"
            }
          }
        }
      },
      "Totals": {
        "type": "object",
        "properties": {
          "net": {
            "$ref": "#/components/schemas/Clp"
          },
          "exempt": {
            "$ref": "#/components/schemas/Clp"
          },
          "iva": {
            "$ref": "#/components/schemas/Clp"
          },
          "other_taxes": {
            "$ref": "#/components/schemas/Clp",
            "description": "Zero on every document except a tipo 46 with `withholding`, where it carries the retención NEGATED (e.g. `-1891` when the IVA retained is `1891`) — the only sign that keeps `total = net + exempt + iva + other_taxes` true for every document, withheld or not."
          },
          "total": {
            "$ref": "#/components/schemas/Clp"
          }
        }
      },
      "List": {
        "type": "object",
        "required": [
          "object",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "url": {
            "type": "string"
          },
          "data": {
            "type": "array",
            "items": {}
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "OptinMethod": {
        "type": [
          "string",
          "null"
        ],
        "enum": [
          "en_persona",
          "telefono",
          "email",
          "escrito",
          "otro",
          null
        ],
        "description": "How the person actually agreed to be messaged. `en_persona` = in person or at the point\nof sale; `telefono` = by phone, recorded by whoever took the call; `email` = by email or\na web form; `escrito` = on paper, signed; `otro` = something else, said plainly rather\nthan forced into the nearest fit.\n\nWhatsApp's policy is that holding a number is not consent — \"an affirmative, disclosed\nopt-in action is required\" — so an attestation without a method is refused with\n`400 optin_method_required`.\n"
      },
      "CounterpartyContactOptin": {
        "type": "object",
        "description": "The attestation, as an event rather than a flag. Either all of `attested_at`,\n`attested_by` and `method` are present, or none of them is.\n",
        "properties": {
          "attested": {
            "type": "boolean"
          },
          "attested_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "attested_by": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who recorded it — the dashboard session or the API key that posted it."
          },
          "method": {
            "$ref": "#/components/schemas/OptinMethod"
          },
          "first_message_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the first notification went to this contact. The first message is the one that\ncarries the opt-out line; `null` means none has been sent yet.\n"
          }
        }
      },
      "CounterpartyContact": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "counterparty_contact"
          },
          "id": {
            "type": "string",
            "examples": [
              "cnt_01K2R7Q4XW9M3B8ZC5YHTVJD6N"
            ]
          },
          "empresa_id": {
            "type": "string"
          },
          "counterparty_rut": {
            "$ref": "#/components/schemas/Rut"
          },
          "counterparty_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": "string",
            "description": "The person. A notification addressed to a company is addressed to nobody."
          },
          "phone": {
            "type": "string",
            "description": "E.164, with the leading `+`.",
            "examples": [
              "+56912345678"
            ]
          },
          "primary": {
            "type": "boolean",
            "description": "At most one per counterparty, enforced by a partial unique index. In v1 ONLY the\nprimary is notified; the rest are an address book you can promote from.\n"
          },
          "opt_in": {
            "$ref": "#/components/schemas/CounterpartyContactOptin"
          },
          "opted_out_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Set when the number replied STOP or you recorded an opt-out. One-way: an opted-out\ncontact is frozen, and re-adding the same number is `422 contact_opted_out`.\n"
          },
          "opted_out_source": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "inbound_stop",
              "manual",
              null
            ]
          },
          "notifiable": {
            "type": "boolean",
            "description": "`primary && attested && !opted_out` — the same predicate the emission hook applies,\ncomputed here so a screen and the engine cannot disagree about it.\n"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CounterpartyAddress": {
        "type": "object",
        "description": "The receptor's domicilio, as the SII's own elements: street is DirRecep, comuna is\nCmnaRecep, city is CiudadRecep. Always present as an object, even when every part of it\nis null, so a client never has to guard the parent before reading a part.\n",
        "properties": {
          "street": {
            "type": [
              "string",
              "null"
            ]
          },
          "comuna": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional to the SII's schema in every branch, and filled whenever it is known. The\nfree SII portal lane validates it anyway, so an empresa on that lane is asked for it.\n"
          }
        }
      },
      "Contribuyente": {
        "type": "object",
        "description": "What the SII's public nómina of personas jurídicas says about one RUT, plus a giro short\nenough for `GiroRecep`. A suggestion for a person to confirm, never written into a\ndocument on its own.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "contribuyente"
          },
          "rut": {
            "$ref": "#/components/schemas/Rut"
          },
          "legal_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "The current razón social, as the SII spells it. Null in the rare case the nómina lists activities without a current name."
          },
          "activities": {
            "type": "array",
            "description": "Economic activities in the SII's order. Empty for a company with none registered.",
            "items": {
              "$ref": "#/components/schemas/ContribuyenteActivity"
            }
          },
          "suggested_giro": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "giro": {
                "type": "string",
                "maxLength": 40,
                "examples": [
                  "Compraventa de vehículos"
                ]
              },
              "basis": {
                "type": "string",
                "enum": [
                  "curated",
                  "sii_description"
                ]
              },
              "activity_code": {
                "type": "string",
                "examples": [
                  "451002"
                ]
              }
            }
          },
          "source": {
            "type": "object",
            "properties": {
              "published_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "When the SII published the edition that answered."
              },
              "loaded_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "When Facturia loaded it."
              }
            }
          }
        }
      },
      "ContribuyenteActivity": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "The SII's six-digit activity code.",
            "examples": [
              "451002"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "The SII's official description."
          },
          "vat_affected": {
            "type": "boolean",
            "description": "Whether the activity is afecta a IVA."
          },
          "tax_category": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Categoría tributaria (1 or 2)."
          }
        }
      },
      "CounterpartyIdentity": {
        "type": "object",
        "description": "What an empresa knows about one counterparty: the razón social, giro and domicilio that\ngo into the receptor block of the next document to that RUT.\n\nNo id. There is exactly one identity per (empresa, counterparty RUT), so the RUT is the\nkey — and `source` says whether a person put it there or we read it off an earlier\ndocument.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "counterparty"
          },
          "rut": {
            "$ref": "#/components/schemas/Rut"
          },
          "legal_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "giro": {
            "type": [
              "string",
              "null"
            ]
          },
          "address": {
            "$ref": "#/components/schemas/CounterpartyAddress"
          },
          "source": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where this answer came from. \"api\" is an address you recorded on purpose;\n\"prior_document\" is read off the newest document you emitted to this RUT, which is\ninference and is treated as the weaker source everywhere. Null on a list entry means\nnothing is on file — the list does not do the earlier-document fallback, the\nsingle-counterparty read does.\n",
            "examples": [
              "api"
            ]
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the entry was last written, or when the document it was read off was created."
          }
        }
      },
      "Counterparty": {
        "type": "object",
        "description": "One counterparty of this empresa: the address book entry, if there is one, joined onto\nthe documents already emitted to that RUT and the contacts on file. The LIST of\ncounterparties is still derived and nothing copies a receiver into a row — only a\ndeliberate PATCH does that.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "counterparty"
          },
          "empresa_id": {
            "type": "string"
          },
          "rut": {
            "$ref": "#/components/schemas/Rut"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "The razón social to display — the address book's, else the spelling put on a contact,\nelse whatever the newest document carried. Identical to legal_name, and kept because\nit is what this list has always published.\n"
          },
          "legal_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "giro": {
            "type": [
              "string",
              "null"
            ]
          },
          "address": {
            "$ref": "#/components/schemas/CounterpartyAddress"
          },
          "source": {
            "type": [
              "string",
              "null"
            ],
            "description": "\"api\" when an address is on file for this RUT, null when none is. This list never\nanswers \"prior_document\" — reading that would mean opening the payload of every\ndocument in the scan window; the single-counterparty read is where it lives.\n"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "document_count": {
            "type": "integer",
            "description": "How many notifiable documents (33/34/56/61) were seen in the scan window."
          },
          "contacts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CounterpartyContact"
            }
          },
          "notifiable": {
            "type": "boolean",
            "description": "Whether any contact on file would actually be messaged."
          }
        }
      },
      "CounterpartyUpdateRequest": {
        "type": "object",
        "description": "Partial. A field you do not send keeps its value; a field sent as null is cleared, which\nis how an address recorded by mistake is removed.\n",
        "properties": {
          "legal_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "RznSocRecep, at most 100 characters."
          },
          "giro": {
            "type": [
              "string",
              "null"
            ],
            "description": "GiroRecep, at most 40 characters — half of what the emisor's own giro is allowed, and\nthe cap that bites most often. Refused over the limit, never truncated.\n"
          },
          "address": {
            "$ref": "#/components/schemas/CounterpartyAddress"
          }
        }
      },
      "CounterpartyContactCreateRequest": {
        "type": "object",
        "required": [
          "name",
          "phone"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "phone": {
            "type": "string",
            "description": "E.164. Anything that cannot be brought to that form is `400 invalid_phone`.",
            "examples": [
              "+56912345678"
            ]
          },
          "counterparty_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Overrides the razón social the documents carried, for display only."
          },
          "primary": {
            "type": "boolean",
            "description": "Defaults to true for the first contact of a counterparty, false afterwards."
          },
          "opt_in": {
            "type": "boolean",
            "description": "Your attestation that this person agreed to receive WhatsApp notifications from you.\nOmitting it is a legitimate state: the contact is stored as an address-book entry and\nis never messaged (skip reason `no_optin`).\n"
          },
          "opt_in_method": {
            "$ref": "#/components/schemas/OptinMethod"
          },
          "opt_in_attested_by": {
            "type": "string",
            "description": "Who collected the consent. Defaults to the credential that made the call."
          }
        }
      },
      "CounterpartyContactUpdateRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "counterparty_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "primary": {
            "type": "boolean",
            "description": "`true` demotes whoever currently holds it, in the same call. Two primaries cannot\nexist even momentarily.\n"
          },
          "opt_in": {
            "type": "boolean",
            "description": "`false` clears the attestation — a consent record that can only ever be added to is worse for the person it exists to protect."
          },
          "opt_in_method": {
            "$ref": "#/components/schemas/OptinMethod"
          },
          "opted_out": {
            "type": "boolean",
            "description": "`true` records that the person asked to stop by another channel. `false` is not\naccepted: an opt-out is not reversible through this API.\n"
          }
        }
      },
      "ErrorBody": {
        "type": "object",
        "required": [
          "type",
          "code",
          "message"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "invalid_request_error",
              "authentication_error",
              "permission_error",
              "not_found_error",
              "conflict_error",
              "unprocessable_error",
              "rate_limit_error",
              "sii_error",
              "api_error"
            ]
          },
          "code": {
            "type": "string",
            "examples": [
              "receptor_incomplete"
            ]
          },
          "message": {
            "type": "string",
            "description": "ENGLISH. Machine text, like `code` and `hint` — read `info.description`'s language\nrule. Show it to a person if you must, but branch on `code`: the wording is prose and\nmay be reworded, while `code` carries §5.8's twelve-month promise.\n"
          },
          "hint": {
            "type": "string",
            "description": "The next action, for the identical code and payload named by `error.doc_url` — the same\ntext `GET /error_codes` publishes for this `code`, inlined so a caller does not need a\nsecond request to learn what to do about it. Absent only for a code minted after your\nclient shipped and not yet in your local copy of the dictionary; treat that as \"consult\n`error.message` and `error.doc_url`\", never as a fault.\n"
          },
          "retriable": {
            "type": "boolean",
            "description": "Could the IDENTICAL request succeed later, unchanged? The same field `GET /error_codes`\npublishes for this `code`. Absent under the same rare condition as `hint`.\n"
          },
          "retry_after_seconds": {
            "type": "integer",
            "description": "HOW LONG TO WAIT, when `retriable` is `true`. Present on every refusal that carries a\ncadence, and the same value the `Retry-After` and `Facturia-Poll-After` headers carry.\n\nIt is in the BODY as well as in the headers because a consumer that stored the JSON —\na queue worker replaying a failure, an MCP tool relaying the envelope — no longer has\nthe headers. `retriable: true` with no number was half an answer: it said «come back»\nand left «when» to a guess (#511).\n"
          },
          "param": {
            "type": "string"
          },
          "doc_url": {
            "type": "string",
            "format": "uri"
          },
          "request_id": {
            "type": "string"
          },
          "empresa": {
            "$ref": "#/components/schemas/Rut"
          },
          "existing_id": {
            "type": "string",
            "description": "On conflicts, the id of the object that already exists."
          },
          "sii": {
            "type": "object",
            "description": "Present on `sii_error` — the raw upstream estado.",
            "properties": {
              "estado": {
                "type": "string"
              },
              "glosa": {
                "type": "string"
              },
              "err_code": {
                "type": "string"
              },
              "track_id": {
                "type": "string"
              }
            }
          },
          "details": {
            "description": "Machine-readable context **for this `code`**, and the only part of the envelope that\nsays \"what to do and who does it\". It is where the guided path lives: the emission\nrefusal carries the `pending_action` a human has to perform, the `402` carries the\nURL where that human pays, and the acuse refusal carries the deadline it was taken on.\nReading it saves the second request that would otherwise be needed to learn what the\nfirst response already knew.\n\n**Its shape is a function of `code`, never of the HTTP status**, so branch on `code`\nfirst and read `details` second. The variants below are the ones published today;\n§5.8 applies here as everywhere — a code minted after your client shipped may carry a\nshape you do not recognise, and an unrecognised `details` is never a fault. Absent\nentirely for codes that need no context.\n",
            "oneOf": [
              {
                "$ref": "#/components/schemas/EmissionRefusalDetails"
              },
              {
                "$ref": "#/components/schemas/SubscriptionRequiredDetails"
              },
              {
                "$ref": "#/components/schemas/WindowClosedDetails"
              },
              {
                "$ref": "#/components/schemas/ValidationDetails"
              },
              {
                "$ref": "#/components/schemas/UnknownEndpointDetails"
              },
              {
                "$ref": "#/components/schemas/NoEmpresaReachableDetails"
              },
              {
                "$ref": "#/components/schemas/InsufficientScopeDetails"
              },
              {
                "title": "Other",
                "type": "object",
                "additionalProperties": true,
                "description": "Any other code's context — an artifact refusal's `{status, document_id}`, a\ncaracterización's `{refusals}`, a rejected annulment's `{annulled_by}`. Open by\nconstruction; read what you recognise.\n",
                "not": {
                  "anyOf": [
                    {
                      "required": [
                        "onboarding_status"
                      ],
                      "properties": {
                        "onboarding_status": {}
                      }
                    },
                    {
                      "required": [
                        "checkout_url"
                      ],
                      "properties": {
                        "checkout_url": {}
                      }
                    },
                    {
                      "required": [
                        "reason",
                        "sii_code"
                      ],
                      "properties": {
                        "reason": {},
                        "sii_code": {}
                      }
                    },
                    {
                      "required": [
                        "problems"
                      ],
                      "properties": {
                        "problems": {}
                      }
                    },
                    {
                      "required": [
                        "contract"
                      ],
                      "properties": {
                        "contract": {}
                      }
                    },
                    {
                      "required": [
                        "reachable_empresas"
                      ],
                      "properties": {
                        "reachable_empresas": {}
                      }
                    },
                    {
                      "required": [
                        "required_scope"
                      ],
                      "properties": {
                        "required_scope": {}
                      }
                    }
                  ]
                }
              }
            ]
          }
        }
      },
      "EmissionRefusalDetails": {
        "title": "EmissionRefusalDetails",
        "type": "object",
        "description": "Carried by `422 empresa_not_emitting` — the refusal that governs the whole guided path\n(`POST /documents` and every other live act on an empresa that has not finished\nonboarding). The handoff travels ON THE REFUSAL, the way an MCP `401` carries\n`WWW-Authenticate: … resource_metadata=\"…\"`: an agent that hits this wall needs more than\na code, it needs something it can relay to its human.\n",
        "required": [
          "onboarding_status",
          "onboarding"
        ],
        "properties": {
          "onboarding_status": {
            "$ref": "#/components/schemas/OnboardingStatus"
          },
          "blocked_on": {
            "type": [
              "string",
              "null"
            ],
            "description": "The step key holding everything up, or `null` when nothing is.",
            "examples": [
              "mandato"
            ]
          },
          "onboarding_url": {
            "type": "string",
            "deprecated": true,
            "description": "Where to read the whole checklist — `GET /v1/empresas/{empresa_id}/onboarding`. A\nRELATIVE path, not the absolute URL `format: uri` implied: a caller has to prefix the\nhost itself. **Deprecated — use `onboarding` instead**, which names the identical\nendpoint as `{method,url}`, absolute (#419). Kept, and still populated, for existing\ncallers.\n\nOUT OF `required` since 2026-09 (#495): a deprecated field cannot be the one a\nconsumer is obliged to read.\n"
          },
          "onboarding": {
            "type": "object",
            "description": "The same endpoint as `onboarding_url`, structured (#419, contract (d)) — the shape\n`pending_action.link.mint` already ships. Always present.\n",
            "required": [
              "method",
              "url"
            ],
            "properties": {
              "method": {
                "type": "string",
                "const": "GET"
              },
              "url": {
                "type": "string",
                "format": "uri",
                "description": "Absolute, so this object alone is enough to issue the request."
              }
            }
          },
          "pending_action": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PendingAction"
              },
              {
                "type": "null"
              }
            ],
            "description": "The blocking step's handoff, when the block is a HUMAN's. `null` whenever the blocking\nstep is ours or the SII's — which is the honest answer and a different thing from\n\"there is nothing to do\": it means wait, not act.\n"
          },
          "blocked_by": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "status",
              "onboarding",
              "puesta_en_vivo",
              "subscription",
              null
            ],
            "description": "Present on the `puesta_en_vivo` variant of this refusal, and declared here because it\nwas shipping undeclared (#495). Same vocabulary and same author as\n`Empresa.capabilities.blocked_by` — `emissionEligibility`, the one predicate.\n"
          },
          "next": {
            "type": "object",
            "description": "Present alongside `blocked_by`, and the same object `EmissionCapabilities.next`\ncarries: who moves, and the `{method, url}` for it. On the `puesta_en_vivo` variant\n`who` is `facturia` — nothing is owed by you or by your user.\n"
          }
        }
      },
      "SubscriptionRequiredDetails": {
        "title": "SubscriptionRequiredDetails",
        "type": "object",
        "description": "Carried by `402 subscription_required`. The subscription is per EMPRESA, so the block\nnames the empresa it is about and the URL where a human pays for that one; another\nempresa on the same account is unaffected, and the sandbox is never gated.\n",
        "required": [
          "empresa",
          "empresa_rut",
          "account_plan",
          "subscription_status",
          "checkout_url"
        ],
        "properties": {
          "empresa": {
            "type": "string",
            "examples": [
              "emp_01K2R6ZC4P8QW1VN7T3MHDY9EK"
            ]
          },
          "empresa_rut": {
            "$ref": "#/components/schemas/Rut"
          },
          "empresa_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "account_plan": {
            "type": "string",
            "examples": [
              "free"
            ]
          },
          "subscription_status": {
            "type": "string",
            "examples": [
              "none",
              "past_due",
              "active"
            ]
          },
          "grace_until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "The last day the lapsed subscription was still honoured, when one was running."
          },
          "checkout_url": {
            "type": "string",
            "format": "uri",
            "description": "Where the titular pays, for THIS empresa. Relay it; do not construct it."
          },
          "blocked_by": {
            "type": "string",
            "const": "subscription",
            "description": "The same vocabulary `Empresa.capabilities.blocked_by` uses, so an agent that already\nbranches on that field needs no second code path (#475). Since eligibility is\nevaluated first, the two can no longer disagree.\n"
          },
          "next": {
            "type": "object",
            "description": "WHO MOVES, WHERE, AND HOW YOU LEARN IT IS DONE (#475). This refusal used to carry a\nURL and nothing else, so an agent relaying it had to invent the actor, the role and\nthe way to verify — a human step with no handoff, which is the shape ADR-0011 calls\na P1.\n",
            "properties": {
              "who": {
                "type": "string",
                "const": "human"
              },
              "performed_by": {
                "type": "object",
                "description": "The TITULAR OF THE ACCOUNT, never the representante legal: this is a card, not a\nlegal act. The distinction matters — the mandato and the cumplimiento are the\nrep. legal's and cannot be delegated; a payment can.\n",
                "properties": {
                  "role": {
                    "type": "string",
                    "const": "titular_cuenta"
                  }
                }
              },
              "method": {
                "type": "string",
                "const": "GET"
              },
              "url": {
                "type": "string",
                "format": "uri",
                "description": "The same page as `checkout_url`, in the `{method, url}` shape"
              },
              "check": {
                "type": "object",
                "description": "How an agent learns the human is done, without guessing.",
                "properties": {
                  "method": {
                    "type": "string",
                    "const": "GET"
                  },
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "field": {
                    "type": "string",
                    "examples": [
                      "capabilities.blocked_by"
                    ]
                  },
                  "done_when": {
                    "type": "string",
                    "examples": [
                      "it stops reading `subscription`"
                    ]
                  }
                }
              }
            }
          }
        }
      },
      "UnknownEndpointDetails": {
        "title": "UnknownEndpointDetails",
        "type": "object",
        "description": "Carried by `404 unknown_endpoint` (#458, #535). A guessed URL is the ordinary way an agent\nthat has read no documentation starts, so the refusal answers the two questions it has:\nwhich contract describes the family of paths it aimed at, and — when it missed by a\ncharacter or two — which path we actually serve.\n",
        "required": [
          "contract"
        ],
        "properties": {
          "contract": {
            "type": "string",
            "format": "uri",
            "description": "The contract covering the path that was asked for. `/account/*` and `/dashboard/*` are\nthe BOOTSTRAP document's (`openapi-bootstrap.yaml`) — how you get an account and your\nfirst key, with no key in hand — and everything else is this one. Naming the right one\nper request is the half a shared `hint` cannot do.\n"
          },
          "did_you_mean": {
            "type": "object",
            "description": "The closest declared route, when the requested path is within two edits of one, or\nwhen the path is exact and only the verb is wrong. Absent when nothing is close: a\nconfident wrong pointer is worse than none.\n",
            "required": [
              "method",
              "path",
              "url"
            ],
            "properties": {
              "method": {
                "type": "string",
                "enum": [
                  "GET",
                  "POST",
                  "PATCH",
                  "DELETE"
                ]
              },
              "path": {
                "type": "string",
                "examples": [
                  "/v1/empresas"
                ]
              },
              "url": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "InsufficientScopeDetails": {
        "title": "InsufficientScopeDetails",
        "type": "object",
        "description": "Carried by `403 insufficient_scope` (#498). The missing scope used to live only in\n`error.message`, against this contract's own rule that a program branches on `code` and\nnever on prose — and the hint said «`error.message` names the missing scope», which is\nprose telling a program to parse prose.\n",
        "required": [
          "required_scope",
          "mint"
        ],
        "properties": {
          "required_scope": {
            "$ref": "#/components/schemas/Scope"
          },
          "mint": {
            "type": "object",
            "description": "The call that produces a key carrying it, for the credential you are holding:\n`POST /v1/api_keys` from a root key, `POST /dashboard/api_keys` from an account\nsession. Scopes are fixed at mint time — a key is replaced, never widened.\n",
            "required": [
              "method",
              "url"
            ],
            "properties": {
              "method": {
                "type": "string",
                "const": "POST"
              },
              "url": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "NoEmpresaReachableDetails": {
        "title": "NoEmpresaReachableDetails",
        "type": "object",
        "description": "Carried by `400 empresa_required` when the credential reaches NO empresa at all (#457) —\nthe first state of every account an agent creates. The header is not the problem there:\nthere is nothing to name, so the refusal names the call that creates something to name.\n",
        "required": [
          "reachable_empresas",
          "next"
        ],
        "properties": {
          "reachable_empresas": {
            "type": "integer",
            "const": 0
          },
          "next": {
            "type": "object",
            "required": [
              "who",
              "method",
              "url"
            ],
            "properties": {
              "who": {
                "type": "string",
                "const": "agent"
              },
              "action": {
                "type": "string",
                "const": "create_empresa"
              },
              "method": {
                "type": "string",
                "const": "POST"
              },
              "url": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "WindowClosedDetails": {
        "title": "WindowClosedDetails",
        "type": "object",
        "description": "Carried by the three commercial-response refusals of §11.2 —\n`commercial_response_window_closed`, `commercial_response_not_applicable` and\n`409 commercial_response_already_registered`. The engine's own `reason` is finer-grained\nthan the codes, and travels here rather than becoming a fourth public code.\n",
        "required": [
          "reason",
          "sii_code"
        ],
        "properties": {
          "reason": {
            "type": "string",
            "description": "The engine's refusal reason.",
            "examples": [
              "window_closed",
              "prior_reclamo",
              "doc_type_not_allowed"
            ]
          },
          "sii_code": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The SII's own numeric code for this refusal, where it has one."
          },
          "deadline_at": {
            "type": "string",
            "format": "date-time",
            "description": "The instant the 8-day window closed (§11.2), rendered in America/Santiago and computed\non the same civil day the refusal was taken on. Absent only when the document has no\ndated anchor yet.\n"
          }
        }
      },
      "ValidationDetails": {
        "title": "ValidationDetails",
        "type": "object",
        "description": "Carried by refusals that check a whole payload and report every fault at once rather than\none per round trip — `certification_input_invalid` is the one published today. `error.param`\nstill names the FIRST offending field, so a client that reads only the contract's own field\nhas somewhere to point; the full list is here.\n",
        "required": [
          "problems"
        ],
        "properties": {
          "problems": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "examples": [
                    "invalid_rut"
                  ]
                },
                "field": {
                  "type": "string",
                  "description": "Dotted path, as a caller would write it: `proveedores[\"33:32\"].rut`."
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "$ref": "#/components/schemas/ErrorBody"
          }
        }
      },
      "Account": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "account"
          },
          "id": {
            "type": [
              "string",
              "null"
            ],
            "description": "`acct_…`. NULLABLE, and null for the credentials that predate the account system\n(migration 001's internal clients) — a key with no account resolves to no id here.\nEvery credential a self-serve caller can obtain carries one.\n"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "The account's `company_name`, falling back to `name`. NULLABLE, and **null is the\nordinary case for an agent's cold start** (#473): `POST /account/register` takes\nneither field, so an account created by an agent has no name until a human sets one\nin the panel. It was declared `type: string` while the wire sent `null`, which a\nstrict generated client rejects on the very first call of the guided path.\n"
          },
          "key": {
            "$ref": "#/components/schemas/ApiKey"
          },
          "empresas_ready": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Of the empresas `key.empresas` reaches, how many `emissionEligibility` says CAN\nEMIT right now — the same predicate `POST /v1/documents` refuses with and\n`Empresa.capabilities` reports per empresa (#416). An agent holding a `[\"*\"]` key\nover forty empresas answers \"how many can I invoice with today\" in one call instead\nof forty.\n\n`null` means the summary could not be computed this time (never a `0` somebody\nwould act on) — `empresas_total` still reports the credential's reach.\n"
          },
          "empresas_total": {
            "type": "integer",
            "description": "How many empresas this credential can REACH — its own allowlist resolved to real\nrows, not the account's total headcount. An allowlisted key is answered about its\nown reach.\n"
          },
          "limits": {
            "type": "object",
            "properties": {
              "requests_per_minute": {
                "type": "integer"
              },
              "emissions_per_second": {
                "type": "integer"
              }
            }
          }
        }
      },
      "Scope": {
        "type": "string",
        "enum": [
          "documents:read",
          "documents:write",
          "received:read",
          "received:write",
          "empresas:read",
          "empresas:write",
          "folios:write",
          "tax:read",
          "tax:write",
          "webhooks:read",
          "webhooks:write",
          "usage:read",
          "keys:write",
          "mandato:write",
          "mandato:attest",
          "onboarding:write",
          "onboarding:attest"
        ]
      },
      "ApiKey": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "api_key"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "root": {
            "type": "boolean",
            "description": "Root keys carry only `keys:write`: they mint, roll and revoke other keys and can do\nnothing else — no emission, no document reads, no tax data.\n"
          },
          "last4": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Scope"
            }
          },
          "empresas": {
            "type": "array",
            "description": "Allowlist. `[\"*\"]` means every empresa on the account, present and future.",
            "items": {
              "type": "string"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ApiKeyWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ApiKey"
          },
          {
            "type": "object",
            "properties": {
              "secret": {
                "type": "string",
                "description": "Returned once, on creation or roll. Never readable again, not even by a root key.",
                "examples": [
                  "sk_live_qT8vN2wLmR5xKpJ7hYdCbA3s"
                ]
              }
            }
          }
        ]
      },
      "EmpresaStatus": {
        "type": "string",
        "enum": [
          "onboarding",
          "active",
          "suspended",
          "archived"
        ]
      },
      "Origen": {
        "type": [
          "string",
          "null"
        ],
        "enum": [
          "gratuito",
          "mercado",
          "ninguno",
          null
        ],
        "description": "How the empresa emits today, as you declare it. `gratuito` = it uses the SII's own\nSistema de Facturación Gratuita; `mercado` = it already uses a market provider;\n`ninguno` = it does not emit electronically yet. `null` only on empresas created\nbefore the field existed — never declared, and deliberately not backfilled.\n\nIt routes the onboarding copy we show and nothing else. **Whether an empresa may skip\ncertification is decided 100% empirically — a claimed resolución only earns the skip\nonce matched against a real operation actually performed in that capacity, never a\nportal reading — and the value you declare here is never an input to that decision.**\nEnrolment in the free system makes a RUT an emisor of real DTEs without a resolución\nde mercado, so a declared `mercado` we trusted would skip certification for exactly\nthe clients who still need it.\n"
      },
      "TaxpayerIvaShape": {
        "type": [
          "object",
          "null"
        ],
        "description": "Which certification shape the SII put this taxpayer in, and how we know. **Read-only** —\nit is the SII's answer about your empresa, not a setting: no request body can write it.\n\nThe SII decides, by itself, whether a contribuyente has activities afectas a IVA, and\nthat decision issues a DIFFERENT set de pruebas. An `exenta` taxpayer is issued guías de\ndespacho, facturas exentas, a libro de guías and a libro de compras **para exentos** —\nand **no set básico at all** — with boletas tipo 41. An `afecta` one is issued the set\nbásico and the two libros, with boletas tipo 39.\n\n`null` means we have not established it yet. It is never a default: assuming `afecta`\nfor an exento contribuyente would build eleven documents the SII will never review.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "taxpayer_iva_shape"
          },
          "shape": {
            "type": "string",
            "enum": [
              "afecta",
              "exenta"
            ]
          },
          "coniva": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "S",
              "N",
              null
            ],
            "description": "The SII's own answer, verbatim — the hidden `CONIVA` field of the postulación form.\n`null` when we derived the shape from the set de pruebas instead of reading the\nfield, which is a weaker claim and is reported as such rather than smoothed over.\n"
          },
          "source": {
            "type": "string",
            "enum": [
              "pe_datos_empresa",
              "set_file"
            ],
            "description": "`pe_datos_empresa` = a capture of the SII's own answer. `set_file` = a\nclassification of the set de pruebas the SII issued for this empresa: strong\nevidence about the shape, but not a reading of `CONIVA`.\n"
          },
          "observed": {
            "type": "boolean",
            "description": "Whether the `CONIVA` → set-list mapping being applied was captured or is predicted.\nThe exenta mapping was observed in full on 2026-08-21; the afecta one is inferred\nand is reported as such until a `CONIVA=S` page is captured.\n"
          },
          "observed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "expected_sets": {
            "type": "array",
            "description": "The `set_kind` values this taxpayer's set de pruebas round should contain.",
            "items": {
              "type": "string",
              "enum": [
                "basico",
                "exenta",
                "guia",
                "libro_guias",
                "libro_ventas",
                "libro_compras",
                "boleta"
              ]
            }
          },
          "expected_set_titles": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "expected_total_documents": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The `TOTAL` the SII prints for this shape, when it is known."
          },
          "boleta_tipo": {
            "type": [
              "integer",
              "null"
            ],
            "enum": [
              39,
              41,
              null
            ]
          },
          "help": {
            "type": "string",
            "description": "Spanish, customer-facing."
          },
          "detail": {
            "type": "string",
            "description": "Spanish. Where the answer came from."
          }
        }
      },
      "Empresa": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "empresa"
          },
          "id": {
            "type": "string"
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "rut": {
            "$ref": "#/components/schemas/Rut"
          },
          "legal_name": {
            "type": "string"
          },
          "giro": {
            "type": [
              "string",
              "null"
            ],
            "description": "NULLABLE (#496). `POST /v1/empresas` does not require it and stores `null` when it is\nomitted, which is the ordinary create — the same class of lie #473 found on\n`contact_email`, caught by validating a real body against this schema instead of\ncomparing the keys of a hand-written example.\n"
          },
          "acteco": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "address": {
            "$ref": "#/components/schemas/EmpresaAddress"
          },
          "codigo_sucursal": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "The SII branch code (`CdgSIISucur`) this empresa emits from. `null` is casa matriz,\nwhich is most empresas — and it is REPORTED as null rather than omitted, because\n\"we hold no sucursal for you\" is a fact an SFG wizard has to be able to see: the\nPortal MIPYME's own preview refuses a document whose emisor origin is incomplete.\n"
          },
          "contact_email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "description": "NULLABLE, and null is what an empresa created without one reports (#473). It was\ndeclared `type: string` while the wire sent `null` for every empresa that never\nsupplied it — the ordinary case — so a strict generated client (pydantic, quicktype)\nrejected the response of `GET /v1/empresas/{id}`, the read that carries\n`capabilities` and the onboarding handoff.\n"
          },
          "ambiente": {
            "type": "string",
            "enum": [
              "certificacion",
              "produccion"
            ],
            "description": "Which SII environment live emissions target. **READ-ONLY AND DERIVED** — it is not\naccepted on create or update, and sending it is refused with `400 invalid_field`.\n\nThe rule: `emission_channel: sfg` is always `produccion`, because the SII\nhabilitated the empresa when it enrolled in the free system. `emission_channel:\nmercado` is `certificacion` until the SII issues its resolución, which is the only\nthing that moves it — or from the start when a stated `origen: mercado` resolución\nis verified by our own probe.\n\nIt was a request field with `default: produccion` until 2026-09, and the applied\ndefault was the opposite, so a generated client that filled schema defaults created\nuncertified empresas in production. State `origen` and `emission_channel`; read this\nback.\n"
          },
          "emission_channel": {
            "type": "string",
            "enum": [
              "mercado",
              "sfg"
            ],
            "default": "mercado",
            "description": "HOW THIS EMPRESA EMITS TODAY. `mercado` is Facturia's own certified engine; `sfg`\ndrives the SII's free Sistema de Facturación Gratuito on the empresa's behalf, with\nfolios the portal manages.\n\nThe titular states it — it is a preference, not something we can read off the SII —\nand it decides which checklist, which tipos (`sfg` has no boleta form and no tipo\n46) and which `ambiente`. A stored NULL reads as `mercado`, so this is never null.\n"
          },
          "certification_track": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "mercado",
              "sfg",
              null
            ],
            "description": "WHICH CERTIFICATION CHECKLIST GOVERNS IT — a different question from\n`emission_channel`, and the two can legitimately disagree: an empresa can emit\nthrough the free portal while certifying for mercado, which is exactly what the\npilot does.\n\n`null` is a real answer and the honest permanent state of a customer content on the\nfree portal for ever: not pursuing certification. It folds the short SFG checklist.\n"
          },
          "doc_types": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TipoDte"
            }
          },
          "origen": {
            "$ref": "#/components/schemas/Origen"
          },
          "resolucion": {
            "$ref": "#/components/schemas/ResolucionState"
          },
          "resolucion_boletas": {
            "$ref": "#/components/schemas/ResolucionBoletasState"
          },
          "reception_email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "description": "Per-empresa reception address, `<rut>@dte.facturia.cl` — the address the empresa\nregisters with the SII as Mail Contacto Empresas.\n\n`null` FOR A SANDBOX EMPRESA (#450). A sandbox empresa reserves nothing: not the RUT\n(uniqueness is per mode) and not this alias. Publishing one for a scratch empresa\nwould publish an address that routes a real company's supplier documents. Inbound\nreception is a live-mode fact.\n"
          },
          "status": {
            "$ref": "#/components/schemas/EmpresaStatus"
          },
          "iva_shape": {
            "$ref": "#/components/schemas/TaxpayerIvaShape"
          },
          "onboarding": {
            "type": "object",
            "description": "The same derivation `GET .../onboarding` uses (#414), so the two cannot disagree —\nthey used to: `status` was the raw spine column and `next_action_url` filled in only\nwhen it read the literal string `action_required`, which the machine never stores.\n",
            "properties": {
              "status": {
                "$ref": "#/components/schemas/OnboardingStatus"
              },
              "progress": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "blocked_on": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The step key holding this empresa up, or null. Same field as `GET .../onboarding`'s `blocked_on`."
              },
              "next_action_url": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uri",
                "description": "ALWAYS `null` here (contract (d) — never a URL that opens «Falta el enlace»). A\ncapability-token onboarding page is a link scoped to ONE empresa and this object\ndoes not hold one; `url` below is where to get one.\n"
              },
              "url": {
                "type": "string",
                "format": "uri",
                "description": "`GET .../onboarding` for this empresa — the full handoff (`pending_action`,\nincluding `pending_action.link.mint` to acuñar a live action URL) that this\nsummary object does not carry.\n\nABSOLUTE (#473, #419): `https://api.facturia.cl/v1/empresas/{id}/onboarding`, the\nsame origin `pending_action.link.mint.url` and `verification.recheck.url` carry.\nIt was a relative path under a `format: uri` declaration, which is a lie a strict\nclient rejects and a URL an agent cannot dereference.\n"
              }
            }
          },
          "capabilities": {
            "$ref": "#/components/schemas/EmissionCapabilities"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ResolucionState": {
        "type": [
          "object",
          "null"
        ],
        "description": "**«¿YA EMITES CON OTRO PROVEEDOR?»** — the resolución this empresa holds, and whether we\nhave PROVED it (ruling 2026-09-07).\n\nThe SII certifies the *contribuyente*, not the software. An empresa that already invoices\nthrough another provider holds a resolución the SII issued to its RUT, and that resolución\ndoes not stop being valid because it changed vendors — so it can state it on\n`POST /v1/empresas` or `PATCH /v1/empresas/{empresa_id}`, and a verified one skips the whole\ncertification leg.\n\n**`verified` NEVER COMES FROM THE CLAIM.** Stating a resolución changes nothing on its own.\nThe claim waits as `verification_status: pending_verification` until a Facturia operator\nconfirms, for this empresa, the ONE production timbraje that verifies it\n(`POST /v1/empresas/{empresa_id}/resolucion/verify`; `verification_request` is that exact\nrequest). Palena refuses a RUT it has not authorized, so a granted range is the proof\n(`verified`) and a refusal is the answer too (`refused`, with the SII's words in\n`refusal`). Only that act — or the SII issuing the resolución to us at the end of a\ncertification we ran — sets `verified: true`. Nothing is asked of the SII implicitly\n(sii-api#581 D1, 2026-09-23).\n\nBranch on `verified`, never on the presence of `numero`. While it is `false` the empresa is\nwalking the ordinary checklist and `onboarding.blocked_on` says where it is; when the probe\nwas refused, `GET .../onboarding`'s `blocked_reason.resolucion_claim` carries the SII's own\nsentence.\n\n`null` for an empresa that never claimed one and has not been issued one.\n",
        "properties": {
          "numero": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "The número de resolución exenta, as stated or as the SII issued it. `null` for an\nempresa whose resolución we hold only as a date.\n\n**Where to find it:** on your own invoices. Every DTE carátula carries `NroResol` and\n`FchResol`, so the pair is printed on every document this empresa has already emitted\nwith its current provider.\n"
          },
          "fecha": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "The fecha de resolución, `YYYY-MM-DD` — the `FchResol` beside `NroResol` on the same carátula."
          },
          "verified": {
            "type": "boolean",
            "description": "Whether an ACT established this resolución is real — our own production timbraje\nsucceeded for a declared factura tipo, or the SII issued it to us directly. Never\nderived from `numero` being present.\n"
          },
          "verified_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When that act happened. `null` exactly when `verified` is `false`."
          },
          "verification_status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "pending_verification",
              "verified",
              "refused",
              null
            ],
            "description": "Where the claim's verification stands (sii-api#581 D1).\n\n- `pending_verification` — claimed, not yet answered by the SII: waiting for a\n  Facturia operator to confirm the production timbraje, in flight, or an attempt the\n  SII did not answer about the RUT.\n- `verified` — the SII granted the range (or issued the resolución to us).\n- `refused` — the SII answered the confirmed request without a range. Re-stating the\n  resolución with `PATCH` resets it to `pending_verification`.\n\n`null` when nothing was claimed (for example `numero` is `null`) and nothing verified.\n"
          },
          "verification_request": {
            "type": [
              "object",
              "null"
            ],
            "description": "The exact request that verifies (or verified) the claim: ONE production timbraje of\n`quantity` folios of `tipo_dte`. `null` when there is no claim, or the empresa declared\nno factura-family tipo to verify it with.\n",
            "properties": {
              "tipo_dte": {
                "$ref": "#/components/schemas/TipoDte"
              },
              "quantity": {
                "type": "integer",
                "minimum": 1
              },
              "ambiente": {
                "type": "string",
                "enum": [
                  "produccion"
                ]
              }
            }
          },
          "refusal": {
            "type": [
              "object",
              "null"
            ],
            "description": "The SII's answer when `verification_status` is `refused`, verbatim. `null` otherwise.",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              }
            }
          }
        }
      },
      "ResolucionVerifyRequest": {
        "type": "object",
        "required": [
          "confirm",
          "authorized_by"
        ],
        "description": "The operator's explicit confirmation of the one production timbraje.",
        "properties": {
          "confirm": {
            "type": "boolean",
            "description": "Must be `true`. Anything else is `400 confirmation_required` and nothing is sent."
          },
          "authorized_by": {
            "type": "string",
            "minLength": 1,
            "description": "The person confirming. Recorded beside the SII's answer."
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why, in a sentence (the ticket, the customer). Recorded."
          }
        }
      },
      "ResolucionVerification": {
        "type": "object",
        "description": "The recorded confirmation and the one job it queued.",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "resolucion_verification"
            ]
          },
          "empresa": {
            "type": "string"
          },
          "verification_status": {
            "type": "string",
            "enum": [
              "pending_verification"
            ]
          },
          "state": {
            "type": "string",
            "enum": [
              "requested"
            ]
          },
          "request": {
            "type": "object",
            "properties": {
              "tipo_dte": {
                "$ref": "#/components/schemas/TipoDte"
              },
              "quantity": {
                "type": "integer",
                "minimum": 1
              },
              "ambiente": {
                "type": "string",
                "enum": [
                  "produccion"
                ]
              }
            }
          },
          "confirmed_at": {
            "type": "string",
            "format": "date-time"
          },
          "confirmed_by": {
            "type": "string"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "job": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "object": {
                "type": "string",
                "enum": [
                  "job"
                ]
              },
              "type": {
                "type": "string",
                "enum": [
                  "folio_request"
                ]
              },
              "status": {
                "type": "string",
                "enum": [
                  "queued"
                ]
              }
            }
          }
        }
      },
      "ResolucionBoletasState": {
        "type": [
          "object",
          "null"
        ],
        "description": "The boleta half of the same question (ruling 2026-09-07). The SII habilitates boleta\nelectrónica separately from the resolución, and its answer is a DATE with no número:\n«habilitados los documentos de boleta electrónica a contar del …».\n\n`verified` is the same gate `POST /v1/documents` reads before building a 39 or 41, so it is\n`true` both for an empresa that arrived habilitated (its claim proved by a tipo-39/41\ntimbraje) and for one whose habilitación we ran. `fecha: null` with `verified: true` is the\nordinary state of the second kind.\n\n`null` for an empresa that neither claimed a habilitación nor has one.\n",
        "properties": {
          "fecha": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "The «a contar del» date, as the titular stated it. `null` when it was never claimed."
          },
          "verified": {
            "type": "boolean",
            "description": "Whether the SII has habilitated this empresa for boleta electrónica — the gate 39/41 emission reads."
          },
          "verified_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ResolucionClaimRequest": {
        "type": [
          "object",
          "null"
        ],
        "required": [
          "numero",
          "fecha"
        ],
        "description": "**THE RESOLUCIÓN THIS EMPRESA ALREADY HOLDS** (ruling 2026-09-07). Send it when the empresa\nalready invoices electronically through another provider; send `null` to clear it.\n\nBoth halves are printed on the carátula of every documento tributario electrónico the\nempresa emits today (`NroResol`, `FchResol`), so an agent can read them off a recent\ninvoice rather than sending anybody to Mi SII.\n\n**IT IS A CLAIM, NOT A SKIP.** It writes two columns and moves nothing else: the empresa is\nstill created at `mandato_pending`, still needs the certificate and the mandato, and still\ncertifies unless our own production timbraje proves the resolución. That timbraje is made\nonly when a Facturia operator confirms it for this empresa; until then the claim reads\n`resolucion.verification_status: pending_verification`. Read `resolucion.verified` on the\nempresa object for the outcome.\n",
        "properties": {
          "numero": {
            "type": "integer",
            "minimum": 1,
            "description": "The número de resolución exenta (`NroResol`). `400 invalid_field` below 1 — `0` is the\nplaceholder we write ourselves while an empresa is still certifying, so it is never a\nvalid claim.\n"
          },
          "fecha": {
            "type": "string",
            "format": "date",
            "description": "The fecha de resolución (`FchResol`), `YYYY-MM-DD`. `400 invalid_field` if it is in the\nfuture: a resolución the SII has not issued is not one this empresa emits under today.\n"
          }
        }
      },
      "ResolucionBoletasClaimRequest": {
        "type": [
          "object",
          "null"
        ],
        "required": [
          "fecha"
        ],
        "description": "**THE BOLETA HABILITACIÓN THIS EMPRESA ALREADY HOLDS** (ruling 2026-09-07). Send it\nalongside `doc_types` containing 39 or 41; send `null` to clear it.\n\nIt carries no `numero` because the SII issues none for boletas: the habilitación arrives as\n«habilitados los documentos de boleta electrónica a contar del …», and that date is the\nwhole of it. Verified by a tipo-39/41 production timbraje, exactly as the resolución claim\nis; refused, the ordinary habilitación steps appear on the checklist instead.\n",
        "properties": {
          "fecha": {
            "type": "string",
            "format": "date",
            "description": "The «a contar del» date, `YYYY-MM-DD`. `400 invalid_field` if it is in the future: a\nhabilitación that has not started is not one this empresa issues boletas under today.\n"
          }
        }
      },
      "EmissionCapabilities": {
        "type": "object",
        "description": "«May this empresa emit, right now, and if not, who has to do what?» (#416, ADR-0011's\n2026-09-02 addendum §2) — computed by the SAME function `POST /v1/documents` refuses\nemission with, so this object and a `422` can never disagree.\n\n`blocked_by` REPORTS `subscription` ON `GET /v1/empresas/{empresa_id}` and not on the\nLIST (#475, ruling 3): the single-object read asks the billing gate (one indexed read),\nthe list does not, because that would be one billing lookup per row of every page.\nAbsent means «not asked», never «fine». When it does report `subscription`, `next.url`\nis the checkout page for THAT empresa — the same URL the `402` publishes as\n`details.checkout_url`, so the object and the refusal name one page, not two.\n\n**`capabilities.tipos` IS THE EMISSION TRUTH; `doc_types` IS THE DECLARATION** (#493).\nThey answer different questions and used to be published side by side with nothing\nsaying which governs: `doc_types` is what the titular declared they intend to emit — it\ndrives the CERTIFICATION checklist and which folios are requested — and `tipos` is what\nthis empresa can emit right now, which is `doc_types` narrowed to what the current\n`emission_channel` supports (`sfg` has no boleta form and no tipo 46). Branch on\n`tipos`. To emit a tipo that is not in it, add it to `doc_types` with\n`PATCH /v1/empresas/{id}` (and, for 39/41, be on the `mercado` channel).\n",
        "properties": {
          "can_emit": {
            "type": "boolean"
          },
          "tipos": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TipoDte"
            },
            "description": "What this empresa can emit NOW: the declared `doc_types` narrowed to what the current\n`emission_channel` supports. This is the list to branch on before building a\ndocument. A sandbox empresa reports the whole channel list — the sandbox emits every\ntipo without declaring anything.\n"
          },
          "blocked_by": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "status",
              "onboarding",
              "puesta_en_vivo",
              "subscription",
              null
            ],
            "description": "`status` — the empresa itself is suspended or archived.\n`onboarding` — the checklist has not reached `emitting`; see `onboarding.blocked_on`.\n`puesta_en_vivo` — everything the customer owes is done and OURS is not yet: a\nwaiting state, never an errand.\n`subscription` — live emission needs a live subscription for this empresa.\n"
          },
          "next": {
            "type": "object",
            "properties": {
              "who": {
                "type": "string",
                "enum": [
                  "agent",
                  "human",
                  "facturia"
                ],
                "description": "Never `customer` — an agent relays to a human, it does not address one directly."
              },
              "api": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "DEPRECATED, kept in parallel (#419, #493). The endpoint that answers or fixes it,\nas one `\"METHOD /path\"` string. Branch on `method` + `url` below; this field\nstays for the callers that already read it and is never removed inside the\n12-month window §5.8 promises.\n"
              },
              "method": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "GET",
                  "POST",
                  "PATCH",
                  "DELETE",
                  null
                ],
                "description": "The verb of `url` — the structured half of `api`, in the `{method, url}` shape\nevery other handoff in this contract uses.\n"
              },
              "url": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uri",
                "description": "ABSOLUTE URL to perform or read the next action — the endpoint named by `api`,\ndereferenceable as it stands (`https://api.facturia.cl/v1/…`). It was declared as\n\"a page a person can be sent to\" and was `null` in every branch, so nothing that\never shipped read a non-null value: this is the field taking on the job it was\nalways supposed to do. A page for a person is `GET .../onboarding`'s\n`pending_action.action_url`, which is minted per act and single-use.\n"
              }
            }
          }
        }
      },
      "EmpresaCreateRequest": {
        "type": "object",
        "required": [
          "rut",
          "legal_name"
        ],
        "properties": {
          "rut": {
            "$ref": "#/components/schemas/Rut"
          },
          "legal_name": {
            "type": "string"
          },
          "giro": {
            "type": "string"
          },
          "acteco": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "address": {
            "$ref": "#/components/schemas/Address"
          },
          "codigo_sucursal": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "SII branch code (`CdgSIISucur`). Omit, or send null, for casa matriz."
          },
          "contact_email": {
            "type": "string",
            "format": "email"
          },
          "emission_channel": {
            "type": "string",
            "enum": [
              "mercado",
              "sfg"
            ],
            "default": "mercado",
            "description": "How this empresa will emit. `mercado` runs Facturia's certified engine and the\neleven-step certification; `sfg` drives the SII's free Sistema de Facturación\nGratuito for an empresa that is already enrolled there, and runs a short checklist\nwith no certification in it.\n\nChoosing `sfg` for an empresa that is NOT enrolled does not enrol it: the\n`emisor_verificado` step will say so. Choosing `mercado` for one that is happy on\nthe free portal starts a certification it does not need.\n\n`ambiente` is DERIVED from this and is not accepted here — see `Empresa.ambiente`.\n"
          },
          "doc_types": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/TipoDte"
            },
            "default": [
              33,
              34,
              61
            ],
            "description": "WHAT THIS EMPRESA INTENDS TO EMIT — the declaration that drives certification and\nwhich folios are requested; `capabilities.tipos` is what it can emit NOW. Defaults\nto `[33, 34, 61]`; an explicit `[]` is `400 invalid_field`, because an empresa with\nzero tipos can never finish certification.\n\nDeclaring 39 or 41 (boletas) on `emission_channel: sfg` is\n`422 doc_type_not_supported_on_channel`: the SII's free Portal MIPYME exposes no\nboleta form. Boletas need the `mercado` channel, where they also add the\n`habilitacion` step to the checklist.\n"
          },
          "origen": {
            "type": "string",
            "enum": [
              "gratuito",
              "mercado",
              "ninguno"
            ],
            "description": "**REQUIRED IN LIVE MODE** — an `sk_live_` key that omits it gets\n`400 invalid_field` with `param: \"origen\"`, which is the first empresa of every real\ncustomer. It is not in this schema's `required` list because the mode is a property of\nthe credential and not of the body; the operation's `400` declares the refusal (#495).\n\nOptional in test mode, where it defaults to `ninguno` because a sandbox empresa is\nborn emitting and never meets the wizard this field routes. See the `Origen` schema\nfor what it does and does not decide.\n"
          },
          "resolucion": {
            "$ref": "#/components/schemas/ResolucionClaimRequest"
          },
          "resolucion_boletas": {
            "$ref": "#/components/schemas/ResolucionBoletasClaimRequest"
          }
        }
      },
      "EmpresaUpdateRequest": {
        "type": "object",
        "properties": {
          "legal_name": {
            "type": "string"
          },
          "giro": {
            "type": "string"
          },
          "acteco": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "integer"
            },
            "description": "SII activity codes, most specific first; the first one becomes `<Acteco>` on the\nemisor block of every document. An empty array — or null — clears it. Omitting the\nfield leaves the stored value alone.\n"
          },
          "address": {
            "$ref": "#/components/schemas/Address"
          },
          "codigo_sucursal": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "SII branch code (`CdgSIISucur`); null clears it back to casa matriz. Omitting the\nfield leaves the stored value alone — a distinction that matters here, because an\nSFG empresa's Portal MIPYME preview refuses a document whose emisor origin is\nincomplete.\n"
          },
          "contact_email": {
            "type": "string",
            "format": "email"
          },
          "doc_types": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/TipoDte"
            },
            "description": "WHAT THIS EMPRESA INTENDS TO EMIT — the declaration that drives certification and\nwhich folios are requested. Read `capabilities.tipos` for what it can emit NOW.\n\nAn empty array is `400 invalid_field`: an empresa with zero tipos can never finish\ncertification. Omitting the field leaves the stored value alone.\n\nADDING a tipo to an empresa already `emitting` re-opens its checklist at\n`certification_in_progress` — certification and CAF are per tipo, and the SII\nauthorizes only the types you certified. Adding 39 or 41 is what makes the\n`habilitacion` step appear, ON EITHER CHANNEL.\n\nBOLETAS ARE ALLOWED ON THE `sfg` CHANNEL, and declaring one is how you ask for the\nhabilitación. The SII's free Portal MIPYME exposes no boleta form to anybody, so a\n39/41 always leaves through our own engine whatever the empresa emits through —\nwhich means an empresa can keep issuing its facturas on the free portal and have us\nrun the boleta certification for it in the background. The `habilitacion` and\n`folios` steps appear on the free lane's checklist the moment you declare a boleta,\nand `capabilities.tipos` gains 39/41 once the SII habilitates them.\n\nTIPO 46 ON THE `sfg` CHANNEL IS `422 doc_type_not_supported_on_channel`: unlike a\nboleta, it is not a document our engine builds on that lane, so it needs\n`emission_channel: \"mercado\"`. The same refusal fires when `emission_channel` is\nmoved to `sfg` while a 46 is already declared.\n"
          },
          "certification_track": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "mercado",
              "sfg",
              null
            ],
            "description": "WHICH CHECKLIST GOVERNS THIS EMPRESA — settable since #474, because the code has\nalways read it and no surface could state it: `PATCH {emission_channel: \"sfg\"}`\nmoved the tipos, the ambiente and the driver and left the empresa on the ten-step\nmercado checklist with `blocked_on: mandato`, a list an SFG emisor never runs.\n\n`null` means «not pursuing certification» and is a real, permanent answer. Setting\nit to `null` or to `sfg` HIDES the mercado checklist WITHOUT deleting anything: the\nstep records stay exactly as they are and setting the track back to `mercado` shows\nthem again, unchanged. It is a projection, not a destructive write.\n"
          },
          "origen": {
            "type": "string",
            "enum": [
              "gratuito",
              "mercado",
              "ninguno"
            ],
            "description": "Correctable, and the only way an empresa created before the field existed can acquire one."
          },
          "emission_channel": {
            "type": "string",
            "enum": [
              "mercado",
              "sfg"
            ],
            "description": "Correctable, like `origen`: an empresa can move onto the SFG lane or off it.\nMoving to `sfg` sets `ambiente` to `produccion` in the same write, because an\nempresa in the free system is a production emisor. Moving to `mercado` leaves\n`ambiente` alone — the resolución is the only thing that moves it the other way.\n\nIt does NOT change `certification_track` on its own — that is a separate field on\nthis same request body (#474). Those are two facts and each is stated where it\nbelongs.\n"
          },
          "resolucion": {
            "$ref": "#/components/schemas/ResolucionClaimRequest"
          },
          "resolucion_boletas": {
            "$ref": "#/components/schemas/ResolucionBoletasClaimRequest"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ]
          }
        }
      },
      "Certificate": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "certificate"
          },
          "empresa_id": {
            "type": "string"
          },
          "subject_rut": {
            "$ref": "#/components/schemas/Rut"
          },
          "subject_name": {
            "type": "string"
          },
          "issuer": {
            "type": "string"
          },
          "serial": {
            "type": "string"
          },
          "valid_from": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "expiring",
              "expired",
              "revoked"
            ]
          },
          "authorized_empresas": {
            "type": "array",
            "description": "RUTs this certificate is a SII-authorized user of, read from the SII's own\n«Mantención de usuarios autorizados» once the verification has run. Before it has,\nthis falls back to the empresas of your account where the same certificate is\ninstalled — which is a floor, not the SII's answer.\n",
            "items": {
              "$ref": "#/components/schemas/Rut"
            }
          },
          "uploaded_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SfgCredential": {
        "type": "object",
        "description": "Custody metadata for the clave of the «certificado CENTRALIZADO» the SII already holds\nfor an empresa in the Sistema de Facturación Gratuito. It is what authorizes the SII to\nsign a DTE server-side, and it is the ONLY secret this endpoint knows about.\n\nIT IS NOT THE CLAVE TRIBUTARIA. Facturia custodies none, ever.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "sfg_credential"
          },
          "empresa_id": {
            "type": "string"
          },
          "clave_centralizado": {
            "type": "object",
            "description": "Whether we hold one, and since when. That is everything this API will ever say about\nthe value: no length, no hint, no masked form, no verification echo — each of those\nis an oracle, and an oracle over a short password is a way to guess it.\n",
            "properties": {
              "stored": {
                "type": "boolean"
              },
              "stored_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "Null when nothing is stored, and on rows that predate the column."
              }
            }
          },
          "purpose": {
            "type": "string",
            "const": "sfg_emission_centralizado_signing"
          },
          "queued": {
            "type": "boolean",
            "description": "Whether the onboarding tick was enqueued (2026-09, #529). Storing the clave used to\nmove nothing, so the panel said \"la tenemos guardada\" while the checklist on the same\nscreen still said `clave_centralizado: pending` - two answers to one question.\n`false` only on an insert failure; the hourly sweep still catches it.\n"
          },
          "next": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ApiCall"
              },
              {
                "description": "Where to read the result (2026-09, #536). Every other mutation in this flow hands\nback a `next`; this one did not, so an integration had to know from outside the\nresponse that the clave is followed by the delegation check and the puesta en vivo.\n"
              }
            ]
          },
          "pending_action": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PendingAction"
              },
              {
                "type": "null"
              }
            ],
            "description": "The handoff of whatever step comes next, or `null` when the next step is ours. The\nsame object `GET .../onboarding` publishes, from the same builder.\n"
          }
        }
      },
      "SfgCredentialCreateRequest": {
        "type": "object",
        "required": [
          "clave_centralizado"
        ],
        "properties": {
          "clave_centralizado": {
            "type": "string",
            "maxLength": 256,
            "description": "The clave of the empresa's certificado CENTRALIZADO at the SII.\n\nONE FIELD NAME, NO SYNONYMS: `clave`, `password` and `clave_tributaria` are refused\nwith `400 invalid_field` rather than coerced. The one mistake this endpoint exists to\nmake impossible is a customer handing us their SII portal password under a field name\nwe were relaxed about.\n"
          }
        }
      },
      "MandatoCreateRequest": {
        "type": "object",
        "description": "Omit the whole body to have Facturia collect the signature instead; the response then\ncarries a single-use `signing_url`. Send `representante_legal` to record an acceptance\nyou collected yourself.\n\n**Both shapes are closed (ticket #489).** Which act this call performs is decided by the\nbody, so an unrecognised field is refused with `400 invalid_field` naming it, rather\nthan read as \"the empty body\" — which used to mint a fresh `signing_url` and silently\nkill the link the representante legal was already holding. The mint form accepts\n`requested_by` and nothing else; the acceptance form accepts exactly the fields below.\n",
        "properties": {
          "requested_by": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RequestedBy"
              },
              {
                "description": "Who asked for the link, relayed to the hosted page so the person reading it is\ntold «Este enlace lo pidió <name>» instead of being left to guess. Only valid on\nthe mint form (no `representante_legal`). Defaults to the credential: a session\nis `panel`, an API key is `api` under the key's own name.\n"
              }
            ]
          },
          "representante_legal": {
            "type": "object",
            "required": [
              "rut",
              "name"
            ],
            "properties": {
              "rut": {
                "$ref": "#/components/schemas/Rut"
              },
              "name": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "format": "email"
              }
            }
          },
          "accepted_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the representante legal accepted. Clamped to now if it is in the future."
          },
          "accepted_ip": {
            "type": "string"
          },
          "accepted_user_agent": {
            "type": "string"
          },
          "terms_version": {
            "type": "string"
          },
          "evidence": {
            "type": "object",
            "description": "The same facts in the shape a signing page naturally produces. Accepted as an\nalternative to the flat fields above, and kept verbatim in the rendición de\ncuentas alongside whatever we recorded ourselves.\n",
            "properties": {
              "method": {
                "type": "string"
              },
              "ip": {
                "type": "string"
              },
              "user_agent": {
                "type": "string"
              },
              "timestamp": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "Mandato": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "mandato"
          },
          "empresa_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "signed",
              "revoked",
              "none"
            ],
            "description": "`pending` - a signing link is outstanding. `signed` - accepted (check `terms_current`\nbefore trusting it). `revoked` - clausula 7.1 was exercised; the empresa is\nsuspended. `none` - nobody has been asked yet. `none` is only ever returned by\n`GET`, and it is a state rather than a `404`: the empresa exists.\n"
          },
          "terms_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "terms_hash": {
            "type": [
              "string",
              "null"
            ],
            "description": "`sha256:…` over the exact bytes accepted, snapshotted from `GET /v1/mandato_terms`\nat the moment of acceptance. It does not follow later amendments of the document.\n`null` when `status` is `none`.\n"
          },
          "terms_current": {
            "type": "boolean",
            "description": "`false` when `terms_version`/`terms_hash` no longer match the CURRENT\n`GET /v1/mandato_terms`. On a `signed` mandato this means the onboarding\npipeline has already stopped trusting the acceptance and is asking for a\nfresh one — POST again to clear it.\n"
          },
          "representante_legal": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "rut": {
                "$ref": "#/components/schemas/Rut"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "email": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "signing_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Present when Facturia is collecting the signature on your behalf. Returned EXACTLY\nONCE: only a hash of its token is stored, so no later read can reproduce it.\n"
          },
          "signing_expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "signed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "acceptance_method": {
            "type": [
              "string",
              "null"
            ],
            "description": "How the acceptance reached us. `hosted` is an *aprobacion*: a human of the required\nrole completed a page that showed them exactly what they were signing, which is the\nonly form of explicit approval Facturia records. `api` is an *atestacion*: an\nintegrator collected the signature in its own flow and asserted it over the API,\nbehind the dedicated `mandato:attest` scope. `dashboard` is the titular signing in\nsession on the same page component.\n\nPublished so an auditor can tell the two apart without opening the database. Free\ntext, because a later channel should not need a schema change to be recordable.\n"
          },
          "revoked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "superseded_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When a newer version of the terms made this acceptance history (ticket #399). A superseded acceptance is kept as a record and never counts as current; `terms_current` is the field that says whether the signature on file still binds. `null` while the acceptance is current."
          },
          "pending_action": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PendingAction"
              },
              {
                "type": "null"
              }
            ],
            "description": "Present only on the `POST` that mints a `signing_url`, with `action_url` set to that\nsame live URL: the mint is the one moment an address that works and the instructions\nto relay with it exist together. The same object `GET .../onboarding` publishes for\nthe `mandato` step.\n"
          },
          "revocation_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Present only on the `POST` that RECORDS an acceptance. Cláusula 7.1's own address,\nminted in the same act and returned once: the person who just signed leaves holding\nthe thing the contract promises them. `null` when it could not be minted — the\nsignature stands either way, and `POST .../mandato/revocation_link` re-mints it.\n"
          },
          "receipt": {
            "type": "object",
            "description": "Present only on the `POST` that RECORDS an acceptance (ticket #527). What the page is\nallowed to SAY about the copy — it used to promise «te mandamos una copia a …» from\nthe mere presence of an address, while the log at that second said no mail provider\nwas configured.\n",
            "properties": {
              "email": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "email_delivered": {
                "type": "boolean",
                "description": "What the transport reported. `false` means say so and offer the download — never «it is on its way»."
              },
              "error": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "English, for logs. Null when delivered."
              },
              "terms_version": {
                "type": "string"
              },
              "terms_hash": {
                "type": "string"
              },
              "terms_url": {
                "type": "string",
                "format": "uri",
                "description": "`GET /v1/mandato_terms?version=<the version accepted>` — the exact bytes signed, still fetchable after a newer version becomes current."
              }
            }
          },
          "replaced": {
            "type": "boolean",
            "description": "Present only on the `POST` that mints a `signing_url` (ticket #489). `true` when a\nlive, unused signing link existed and this call revoked it — re-minting is allowed,\nbecause somebody asking for a link that works must get one, but the response now\nsays what it cost so an agent can tell the human that the older link stopped\nworking instead of letting them discover it.\n"
          },
          "requested_by": {
            "type": [
              "object",
              "null"
            ],
            "description": "Who this mint was attributed to, as the hosted page will report it. `null` when the\ncaller did not say and no credential could be read.\n",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "agent",
                  "panel",
                  "api"
                ]
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        }
      },
      "MandatoRevocationLink": {
        "type": "object",
        "description": "The cláusula 7.1 page, minted for the representante legal (ticket #465).\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "onboarding_link"
          },
          "empresa_id": {
            "type": "string"
          },
          "step": {
            "type": "string",
            "const": "mandato_revocation"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Returned EXACTLY ONCE — only a SHA-256 of the token is stored, so no later read can\nreproduce it. Send it to the person who signed.\n"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "A century out. The right this link addresses does not expire, so a link that\naddresses it may not either.\n"
          },
          "requires": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The second factor the page will ask for — `rep_legal_rut`, the RUT recorded on the\nacceptance. Named, never echoed: this response would otherwise turn the factor into\na prompt.\n"
          },
          "role": {
            "type": "string",
            "const": "representante_legal"
          }
        }
      },
      "OnboardingLinkCreateRequest": {
        "type": "object",
        "required": [
          "step"
        ],
        "properties": {
          "step": {
            "type": "string",
            "description": "Which hosted page the link opens: any step of THIS empresa's checklist that needs a\nhuman - `actor: customer` (they perform it) or `stall_actor: customer` (we perform it\nand it waits on their decision). `mandato` is deliberately absent: its link is\nsingle-use and comes from `POST /empresas/{empresa_id}/mandato` with an empty body.\nRead the valid values off `GET /empresas/{empresa_id}/onboarding`.\n"
          },
          "requested_by": {
            "$ref": "#/components/schemas/RequestedBy"
          }
        }
      },
      "RequestedBy": {
        "type": "object",
        "description": "Who is asking for this link, as YOU describe yourself (2026-09, #459). Optional, and\nrendered to the person who receives the link: \"Este enlace lo pidio Claude Desktop el\n4 de septiembre\" instead of \"lo genero una integracion o un agente en nombre de tu\ncuenta\", which was the only sentence available for every agent that has ever existed.\n\nA CLAIM, NOT A CREDENTIAL. It changes nothing about authorization and is never trusted for\na decision; the credential your request actually arrived on is recorded separately. Send\n`{\"kind\": \"agent\", \"name\": \"<your client name>\"}` when you are an agent acting for a\nperson.\n",
        "required": [
          "kind"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "agent",
              "panel",
              "api"
            ]
          },
          "name": {
            "type": "string",
            "maxLength": 120,
            "description": "Your own name for yourself. Trimmed and truncated; never interpreted."
          }
        }
      },
      "OnboardingLink": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "onboarding_link"
          },
          "empresa_id": {
            "type": "string"
          },
          "step": {
            "type": "string"
          },
          "pending_action": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PendingAction"
              },
              {
                "type": "null"
              }
            ],
            "description": "The step's handoff, with `action_url` set to the URL this response just minted. The\nsame object `GET /empresas/{empresa_id}/onboarding` publishes for the step, plus the\nlive address - so the instructions an agent relays cannot differ between the two.\n"
          },
          "state": {
            "type": "string",
            "enum": [
              "valid",
              "expired",
              "revoked"
            ],
            "description": "`revoked` outranks `expired`: a link somebody cancelled should say so rather than\nblame the clock.\n"
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "The address to send the representante legal, token included. Returned EXACTLY ONCE,\nat the mint: only a SHA-256 of the token is stored, so no later read can reproduce\nit.\n"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "opened_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When a human first rendered the page. Evidence that the instruction was delivered,\nwhich is a different claim from \"we sent it\". Stamped once, not on every reload.\n"
          },
          "acted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "revoked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "replaced": {
            "type": "boolean",
            "description": "Whether this mint RETIRED a link that was already live (2026-09, #459).\n\nRe-minting revokes - a customer pressing \"send it again\" is asking for a link that\nworks - and it used to be silent, so an agent that minted a second time was never told\nabout the URL now sitting dead in a representante legal's inbox. `false` on a first\nmint. Read `link.status` on `GET .../onboarding` BEFORE minting to avoid needing this.\n"
          }
        }
      },
      "GoLiveRequest": {
        "type": "object",
        "properties": {
          "authorized": {
            "type": "boolean",
            "description": "Present at all, this states the decision on the titular's behalf - an *atestacion*,\nbehind `onboarding:attest`. `false` withdraws an authorization that has not been\nacted on yet; once the declaracion is filed the call is refused with\n`409 cumplimiento_already_filed`.\n\n**No default.** Omit the whole body to mint an approval link for the titular\ninstead. It used to default to `true`, which meant a caller who sent no body\nsilently authorized a tax consequence.\n"
          }
        }
      },
      "GoLiveApproval": {
        "type": "object",
        "description": "The enlace de aprobacion for the titular, answered by `POST .../go_live` with no body.\nSingle use and seven days - it records a decision with a tax consequence, so a link that\nkept working afterwards would be an unattended second chance to change the month.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "go_live_approval"
          },
          "empresa_id": {
            "type": "string"
          },
          "approval_url": {
            "type": "string",
            "format": "uri",
            "description": "Returned EXACTLY ONCE - only a SHA-256 of its token is stored, so no later read can\nreproduce it. Re-minting revokes the previous link.\n"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "role": {
            "type": "string",
            "const": "titular_cuenta",
            "description": "Whose decision this is. The month the IECV obligation starts in is the account\nholder's to choose - not the representante legal's, and not the agent's.\n"
          }
        }
      },
      "GoLiveAuthorization": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "go_live_authorization"
          },
          "empresa_id": {
            "type": "string"
          },
          "authorized": {
            "type": "boolean"
          },
          "authorized_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "authorized_by": {
            "type": [
              "string",
              "null"
            ],
            "description": "`rep_legal` when it came from the hosted page, `account_session` from the\ndashboard, `api_key` from an integrator.\n"
          },
          "filed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the Declaracion de Cumplimiento was actually filed."
          },
          "accepted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "sii_state": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "not_ready",
              "ready",
              "already_declared",
              "unknown",
              null
            ],
            "description": "What the SII last said about whether this empresa may declare at all. Published\nbecause \"I authorized it and nothing happened\" is the obvious next question.\n"
          },
          "sii_paso": {
            "type": [
              "string",
              "null"
            ],
            "description": "The step the SII says is still open, when it says so."
          }
        }
      },
      "CertificationSetFile": {
        "type": "object",
        "description": "The tenant's set de pruebas as we read it. A SUMMARY, deliberately: the door reads the\nfile's skeleton - encoding, section headers, their kinds, caso headers, the atencion -\nand the round re-parses it in full when it builds. So `problems[]` here carries\nfile-level trouble only; anything about a particular caso surfaces on the round.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "certification_set_file"
          },
          "empresa_id": {
            "type": "string"
          },
          "atencion": {
            "type": [
              "string",
              "null"
            ],
            "description": "The SII's own identifier for this set - the first numbered section's."
          },
          "encoding": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "iso-8859-1",
              "utf-8",
              null
            ],
            "description": "What the bytes actually were. ISO-8859-1 is what the SII serves; a file somebody\nre-saved out of an editor is detected rather than mangled.\n"
          },
          "sections": {
            "type": "array",
            "description": "On the upload, the whole table. On the read, the section KINDS only - which is\nwhat decides the sets this empresa must pass.\n",
            "items": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string"
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "basico",
                    "exenta",
                    "guia",
                    "libro_guias",
                    "libro_ventas",
                    "libro_compras",
                    "boleta",
                    "unknown"
                  ]
                },
                "atencion": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "casos": {
                  "type": "integer"
                }
              }
            }
          },
          "casos": {
            "type": "integer"
          },
          "problems": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "empty_file",
                "no_sections",
                "no_casos",
                "unknown_section"
              ]
            }
          },
          "ingested_via": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "api",
              "dashboard",
              null
            ]
          },
          "sha256": {
            "type": [
              "string",
              "null"
            ],
            "description": "The bytes' identity. The storage path is private."
          },
          "bytes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "captured_at": {
            "type": "string",
            "format": "date-time"
          },
          "stored": {
            "type": "boolean",
            "description": "False when the row was written and the file itself could not be kept. Said out\nloud because the round reads the BYTES and would find nothing.\n"
          }
        }
      },
      "CertificationInputs": {
        "type": "object",
        "description": "Everything a round needs that is neither in the set file nor on the empresa. The slot\nnames are the ENGINE's and are never renamed for presentation: what you store here is\nliterally what the builder reads, and the round's own problems speak in these words.\nAn unrecognised key is refused rather than ignored.\n",
        "properties": {
          "receptor": {
            "type": [
              "object",
              "null"
            ],
            "description": "The party the test documents are issued to. `giro` is measured against\n`GiroRecep` (40), which is shorter than `GiroEmis` (80) - an emisor's own name of\ntrade is frequently illegal in this slot.\n",
            "properties": {
              "rut": {
                "type": "string"
              },
              "razonSocial": {
                "type": "string"
              },
              "giro": {
                "type": "string"
              },
              "direccion": {
                "type": "string"
              },
              "comuna": {
                "type": "string"
              },
              "ciudad": {
                "type": "string"
              }
            }
          },
          "giro_recep_emisor": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 40,
            "description": "A ≤40-character abbreviation of the EMISOR's own giro, for the ONE place a\ntraslado interno guia writes it back: the receptor is the emisor itself\n(a traslado interno moves goods between the empresa's own premises), so its giro\nis what would be written into `GiroRecep` - which caps at 40 where the emisor's\nown `GiroEmis` allows 80.\n\nAbsent, and the emisor's giro fits within 40 characters anyway: written verbatim,\nsame as always. Absent, and it does not fit: `GiroRecep` is optional in the\nschema, so the element is OMITTED rather than truncated, and the round raises\n`giro_recep_omitted` so the omission is visible rather than silent - this is the\nexact reparo the SII drew over the pilot's own 65-character giro. Present: written\nverbatim, and refused - never truncated - if it does not fit either.\n"
          },
          "text_correction": {
            "type": [
              "object",
              "null"
            ],
            "description": "The CodRef-2 correction. Written as \"Dice: {says} y debe decir: {shouldSay}\" into\n`RazonRef`, which caps at 90 - 21 of them are the scaffolding, so the two halves\nshare 69. Measured on the composition, and never truncated for you.\n",
            "properties": {
              "says": {
                "type": "string"
              },
              "shouldSay": {
                "type": "string"
              }
            }
          },
          "proveedores": {
            "type": [
              "object",
              "null"
            ],
            "description": "Who issued each purchase, keyed `<tipoDte>:<folio>` - the two things the set\nprints for every row. A RUT whose verifier digit fails modulo 11 is one of the two\ndocumented causes of an LRH \"Descuadrado\", found after the folios are spent.\n`razonSocial` is measured against the LIBRO's `RznSoc` (50), not the DTE's 100.\n",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "rut": {
                  "type": "string"
                },
                "razonSocial": {
                  "type": "string"
                }
              }
            }
          },
          "iva_no_rec_codes": {
            "type": [
              "object",
              "null"
            ],
            "description": "Why a purchase loses its credito. LibroCV_v10.xsd enumerates 1,2,3,4,9.",
            "additionalProperties": {
              "type": "integer",
              "enum": [
                1,
                2,
                3,
                4,
                9
              ]
            }
          },
          "libro_treatments": {
            "type": [
              "object",
              "null"
            ],
            "description": "The IVA treatment of a purchase the set does not classify. \"unknown\" is the\nplanner's word for \"the set said nothing\" and \"exento_sin_iva\" is arithmetic, so\nneither is an input.\n",
            "additionalProperties": {
              "type": "string",
              "enum": [
                "credito",
                "iva_uso_comun",
                "iva_no_recuperable",
                "retencion_total",
                "sin_derecho_credito"
              ]
            }
          },
          "libro_guias": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "folio_notificacion": {
                "type": "integer",
                "default": 3,
                "description": "PREFILLED at 3 - the number continuing the 1 and 2 the libro de ventas and the\nlibro de compras carry. Mandatory in LibroGuia_v10.xsd, so it cannot be\nomitted; a round that uses the default raises\n`libro_guias_folio_notificacion_defaulted` rather than passing in silence.\n"
              },
              "facturas": {
                "type": "object",
                "description": "The factura that invoiced a guia the set marks \"facturada\", by the guia's caso\nid. LibroGuia_v10.xsd has no \"facturada\" flag - the TpoDocRef/FolioDocRef/\nFchDocRef triple IS the statement - and the set never names the factura.\n",
                "additionalProperties": {
                  "type": "object",
                  "properties": {
                    "tipoDte": {
                      "type": "integer"
                    },
                    "folio": {
                      "type": "integer"
                    },
                    "fecha": {
                      "type": "string",
                      "format": "date"
                    }
                  }
                }
              }
            }
          },
          "factor_proporcionalidad": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "The set prints it as \"EL FACTOR DE PROPORCIONALIDAD DEL IVA ES DE 0.60\". It\ndecides how much credito is claimed, so it is never guessed.\n"
          },
          "libro_boleta_folio_notificacion": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The SII-issued \"Folio de la Notificacion con que se Solicita el Libro Especial\".\nNO DEFAULT, and there will not be one - nothing can derive it, and a boleta round\nrefuses without it.\n"
          },
          "boleta_unidades": {
            "type": [
              "object",
              "null"
            ],
            "description": "`UnmdItem` by `<casoId>:<nroLinDet>`, for the one thing a set can demand that its\nown schema forbids - a unidad longer than four characters. Refused, never\ntruncated: \"mens\" is not a unit.\n",
            "additionalProperties": {
              "type": "string",
              "maxLength": 4
            }
          },
          "boleta_muestras": {
            "type": [
              "array",
              "null"
            ],
            "description": "The ten everyday boletas the SII's boleta review asks for, describing what this\nempresa actually sells at prices it actually charges. Fewer than ten is an\nincomplete request and is refused; more is fine.\n",
            "items": {
              "type": "object",
              "properties": {
                "nombre": {
                  "type": "string"
                },
                "monto": {
                  "type": "integer",
                  "description": "The GROSS total in pesos - the number the customer would pay."
                },
                "unidad": {
                  "type": "string",
                  "maxLength": 4
                }
              }
            }
          },
          "permiso_usuario_rut": {
            "type": [
              "string",
              "null"
            ],
            "description": "The natural person the permisos probe asks \"Mantencion de usuarios\" about, when\nthe custodied certificate encodes no RUT of its own.\n\nTHE ONLY SLOT HERE THAT IS NOT A DOCUMENT FACT, and the only one no builder reads.\nEstado 106 \"Usuario sin permiso de envio\" is about the RUT the SII token was\nminted for, so the probe reads it out of the certificate first and falls back to\nthis. Absence does NOT refuse a round: the round still builds and signs, and it is\nthe permisos gate that parks, reporting `no_target` with the remedy attached.\n\nValidated modulo 11 like every other RUT here. `null` clears it. Never the\nempresa's own RUT - that table lists natural persons, so an empresa RUT would find\nnobody and report the sender as unlisted.\n"
          }
        }
      },
      "Certification": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "certification"
          },
          "empresa_id": {
            "type": "string"
          },
          "round": {
            "type": "integer",
            "description": "The round the last run built. 1 with a null `last_run_at` means \"we have not\nattempted\", which is not the same as \"we are on the first attempt\".\n"
          },
          "periodo": {
            "type": [
              "string",
              "null"
            ]
          },
          "set_file_ready": {
            "type": "boolean"
          },
          "last_run_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "detail": {
            "type": [
              "string",
              "null"
            ]
          },
          "set_file": {
            "$ref": "#/components/schemas/CertificationSetFile"
          },
          "inputs": {
            "$ref": "#/components/schemas/CertificationInputs"
          },
          "slots": {
            "type": "array",
            "description": "The slot vocabulary as the engine knows it, with what this empresa has supplied.\nPublished so a form cannot offer a slot the engine ignores nor hide one it needs.\n",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string"
                },
                "shape": {
                  "type": "string",
                  "enum": [
                    "object",
                    "row_map",
                    "line_map",
                    "caso_map",
                    "number",
                    "rut",
                    "list"
                  ]
                },
                "lane": {
                  "type": "string",
                  "enum": [
                    "set",
                    "boleta"
                  ]
                },
                "prefilled": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "refuses_with": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "notice_when_defaulted": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "supplied": {
                  "type": "boolean"
                }
              }
            }
          },
          "submittable": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Null until a build has happened at all - not the same as false."
          },
          "problems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CertificationFinding"
            }
          },
          "notices": {
            "type": "array",
            "description": "What the build DECIDED rather than refused. A notice never blocks a round; it is\nhere because we chose it on the empresa's behalf and a rendicion has to be able to\nshow that.\n",
            "items": {
              "$ref": "#/components/schemas/CertificationFinding"
            }
          },
          "envios": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CertificationEnvio"
            }
          },
          "boleta_round": {
            "type": [
              "object",
              "null"
            ],
            "description": "The boleta lane's own checkpoint, when this empresa has one."
          }
        }
      },
      "CertificationFinding": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "ref": {
            "type": [
              "string",
              "null"
            ],
            "description": "The caso or libro row it belongs to."
          },
          "message": {
            "type": [
              "string",
              "null"
            ],
            "description": "Null when the build record could not be read. The code is always present; a null\nmessage means \"we know this happened and could not read the sentence about it\".\n"
          }
        }
      },
      "CertificationEnvio": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "certification_envio"
          },
          "set_kind": {
            "type": "string"
          },
          "round": {
            "type": "integer"
          },
          "periodo": {
            "type": [
              "string",
              "null"
            ]
          },
          "channel": {
            "type": "string",
            "enum": [
              "dte",
              "boleta"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "built",
              "blocked",
              "uploaded",
              "failed"
            ]
          },
          "driver": {
            "type": "string"
          },
          "dry_run": {
            "type": "boolean",
            "description": "True means the envelope was built and NOT sent."
          },
          "track_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "estado": {
            "type": [
              "string",
              "null"
            ],
            "description": "The ENVIO estado. `SOK` here means \"envio recibido\" - arrived, not judged. The\nset's own verdict is `verdict` below, where `SOK` means approved.\n"
          },
          "estado_glosa": {
            "type": [
              "string",
              "null"
            ]
          },
          "envio_outcome": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "accepted",
              "rejected",
              "pending",
              "unknown",
              null
            ]
          },
          "folios": {
            "type": "object",
            "description": "Every folio this round spent, per tipo. Irreversible - a folio the SII has judged\ncan never carry another document.\n",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "integer"
              }
            }
          },
          "caso_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "envelope_sha256": {
            "type": [
              "string",
              "null"
            ]
          },
          "envelope_bytes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "declared_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "declare_verdict": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "declared",
              "already_declared",
              "refused",
              "unreadable",
              null
            ],
            "description": "The Declarar Avance answer. `already_declared` is a SUCCESS: reading a repeat as a\nfailure would rebuild a round the SII already accepted, burning folios to fix\nnothing.\n"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "verdict": {
            "type": [
              "object",
              "null"
            ],
            "description": "The SET verdict, which arrives after the SII reviews and may never arrive at all.\nNull is \"not reviewed\", never \"rejected\".\n",
            "properties": {
              "outcome": {
                "type": "string",
                "enum": [
                  "accepted",
                  "rejected",
                  "pending",
                  "unknown"
                ]
              },
              "estado": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "reparos": {
                "type": "integer"
              },
              "captured_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "OnboardingEvidence": {
        "type": "object",
        "description": "One act the onboarding pipeline performed on the empresa's behalf - the consultable\nhalf of the mandato's clausula 4.4 (rendicion de cuentas).\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "onboarding_evidence"
          },
          "id": {
            "type": "string"
          },
          "empresa_id": {
            "type": "string"
          },
          "step": {
            "type": "string"
          },
          "tipo_dte": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Set on the four certification gates, which the SII evaluates per type."
          },
          "kind": {
            "type": "string",
            "description": "What the artifact is. Known kinds include `probe_html`, `envio_xml`, `track_id`,\n`verdict`, `declare_receipt`, `caf_xml`, `set_file`, `set_envio`, `set_verdict`,\n`rep_legal_link_issued`, `rep_legal_link_opened`, `mandato_acceptance`,\n`postulacion_attestation`, `go_live_authorized`, `go_live_withdrawn`. The list is\nopen: a later wave adds kinds without a breaking change.\n"
          },
          "captured_at": {
            "type": "string",
            "format": "date-time"
          },
          "retention_until": {
            "type": "string",
            "format": "date-time",
            "description": "Six years from the act. Codigo Tributario prescripcion extraordinaria plus its\nconservation duty, which is also the retention purpose under Ley 21.719. On\nrevocation or traspaso the record freezes read-only and is offered as an export;\nit is never purged early.\n"
          },
          "artifact": {
            "type": [
              "object",
              "null"
            ],
            "description": "Present when bytes were stored. The digest is what makes it checkable.",
            "properties": {
              "sha256": {
                "type": "string"
              },
              "bytes": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "content_type": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "detail": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "OnboardingStatus": {
        "type": "string",
        "enum": [
          "draft",
          "mandato_pending",
          "mandato_signed",
          "certificate_pending",
          "certificate_verified",
          "sii_postulacion_pending",
          "certification_in_progress",
          "cumplimiento_pending",
          "resolucion_pending",
          "folios_pending",
          "emitting",
          "action_required",
          "blocked",
          "suspended"
        ]
      },
      "Onboarding": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "onboarding"
          },
          "empresa_id": {
            "type": "string"
          },
          "mode": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Mode"
              },
              {
                "type": "null"
              }
            ],
            "description": "`test` or `live` — which world this empresa is in (#418). A sandbox empresa is born\n`emitting` with a checklist of `skipped` steps, and a live one at the same `emitting`\nwalked eleven of them; the two bodies are otherwise identical, so a screenshot, a log\nline, or a webhook payload could not tell which one produced it. `null` when the\ncaller building this body did not say — never a guess, because `live` is the answer\nthat gets acted on.\n"
          },
          "status": {
            "$ref": "#/components/schemas/OnboardingStatus"
          },
          "progress": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "blocked_on": {
            "type": [
              "string",
              "null"
            ],
            "description": "The step a human (or a failed requirement) is holding up. Always names a step\npresent in `steps[]`. When `status` is `action_required` that step always carries a\n`pending_action` — which is where its `link.mint` lives, and where its `action_url`\nlives when one is live; when `status` is `blocked` it carries a `blocked_reason`\ninstead, because a blocked empresa has nothing to click — a fact at the SII has to\nchange.\n"
          },
          "pending_action": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PendingAction"
              },
              {
                "type": "null"
              }
            ],
            "description": "The handoff for the step `blocked_on` names, hoisted so \"what is blocking me and what\ndo I tell my human\" is one field read. `null` when the blocking step is OURS or the\nSII's, which is the honest answer and a different thing from \"there is nothing to\ndo\": it means wait, not act.\n"
          },
          "estimated_ready_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Today plus the median business days the remaining steps take (Mon–Fri, no feriado\ncalendar), each one counted as `steps[].eta_business_days` publishes it — so a step\nthat still waits on a person of ours (`execution: operator`) contributes the sitting\nfloor rather than its queue estimate (2026-09-07, #556). An estimate, not a promise:\nthe rep-legal decisions are wall-clock unbounded, and it counts OUR time only — not\nwhat the SII takes to answer the postulación or to issue the resolución. `null` once\n`emitting` or `suspended`, while `blocked`, and while `draft`.\n"
          },
          "requirements": {
            "type": "array",
            "description": "What the SII requires of the contribuyente before it will authorize an emisor,\nas we last read it from the SII. EMPTY until the probe has run once — an empty\nlist means \"we have not looked\", not \"there is nothing to do\". `unknown` means we\nlooked and could not read the answer; it is never treated as `met`.\n",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string",
                  "enum": [
                    "inicio_actividades",
                    "verificacion_actividades",
                    "representante_legal_certificate",
                    "usuario_administrador",
                    "sin_situaciones_pendientes"
                  ]
                },
                "label": {
                  "type": "string",
                  "description": "Spanish. The requirement as the person who has to satisfy it reads it."
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "met",
                    "unmet",
                    "unknown"
                  ]
                }
              }
            }
          },
          "steps": {
            "type": "array",
            "description": "The checklist, in order. `habilitacion` is present only for empresas whose\n`doc_types` include a boleta type (39 or 41) — render what you are given rather\nthan assuming a length. The four certification steps are evaluated PER DOCUMENT\nTYPE by the SII; each appears once here with the worst status across the declared\ntypes, and `certifications[]` breaks the same facts out per type.\n\n**WHICH CHECKLIST YOU GET FOLLOWS `certification_track`, NOT `emission_channel`**\n(#474, corrected 2026-09-04 — this paragraph named the channel and the code has\nalways read the track). An empresa whose `certification_track` is `sfg` or `null`\nreturns a SHORT, PARALLEL checklist — `mandato`, `credential`,\n`clave_centralizado`, `usuario_autorizado`, `emisor_verificado`, `puesta_en_vivo`,\n`emitting` — because a Sistema de Facturación Gratuito empresa is already a\nproduction emisor and never runs postulación, certification, cumplimiento,\nresolución or timbraje. For it `certifications[]` is empty.\n\nThe two fields can legitimately disagree (ADR-0010 §1): the pilot emits through the\nfree portal while certifying for mercado. A new empresa's track is set to its\nchannel at creation; `PATCH /v1/empresas/{empresa_id}` changes each independently,\nand changing the channel alone deliberately leaves the track where it is. Render\nthe steps you are given; do not assume which set you will receive.\n\nTwo of those seven are the facts that ACTUALLY gate emission and used to appear on\nno checklist at all. `clave_centralizado` is the empresa's Sistema de Facturación\nGratuito password: Portal MIPYME asks for it again at the irreversible step of every\nemission, so without it nothing can be issued even with the delegation live and a\ncertificate in custody. `puesta_en_vivo` is OURS — the owner-present canary and the\nper-empresa enablement — and it is `actor: facturia`, rendered as a waiting state\nwith an estimate, never as something a customer must do.\n",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string",
                  "enum": [
                    "mandato",
                    "certificate",
                    "sii_postulacion",
                    "set_de_pruebas",
                    "simulacion",
                    "intercambio",
                    "muestras_impresas",
                    "cumplimiento",
                    "resolucion",
                    "habilitacion",
                    "folios",
                    "credential",
                    "clave_centralizado",
                    "usuario_autorizado",
                    "emisor_verificado",
                    "puesta_en_vivo",
                    "emitting"
                  ]
                },
                "label": {
                  "type": "string",
                  "description": "Spanish. The step's name as a person reads it on a checklist."
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "pending",
                    "in_progress",
                    "action_required",
                    "completed",
                    "failed",
                    "skipped"
                  ],
                  "description": "`failed` means something we run gave up after its retries. It is ours to fix\nand never moves the empresa to `blocked`. `skipped` means the step was never\nrequired — most often because the RUT was already an authorized emisor\nelectrónico, so there was nothing to certify.\n"
                },
                "actor": {
                  "type": "string",
                  "enum": [
                    "facturia",
                    "customer",
                    "sii"
                  ],
                  "description": "`customer` marks a step a PERSON PERFORMS: the mandato, the certificate\nupload, and on the SFG catalog the `clave_centralizado` and the\nusuario-autorizado grant. The certificate upload and the clave happen in OUR\napp rather than at the SII, and are the two an agent can complete on its\nprincipal's behalf (see `pending_action.agent_actions`).\n\n`facturia` DOES NOT MEAN «nothing for anybody to do». Three mercado steps are\nours and still cannot finish without a person: `sii_postulacion`,\n`cumplimiento` and `habilitacion`. We perform each of them at the SII with the\ncustodied certificate — which is the representante legal's own — after that\nperson approves on a hosted page, and the field that says a decision is\npending is `pending_action.approval`, never `actor`. Branch on the handoff.\n\n`puesta_en_vivo` is the opposite case and the one to watch: everything the\ncustomer owes is done and the canary is ours, so it carries no\n`pending_action` at all.\n"
                },
                "help": {
                  "type": "string",
                  "description": "Spanish. One or two sentences explaining the step to the person who owns it —\nwritten for them, not for you. Relay it verbatim rather than paraphrasing:\nseveral of these carry the legal reason an act cannot be delegated.\n"
                },
                "action_url": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uri",
                  "description": "A hosted page you can open RIGHT NOW, or `null`. Non-null only in the response\nthat minted the link (`POST .../onboarding/links`, or `POST .../mandato` with an\nempty body): the pages are reached by a capability token stored only as a hash,\nso a read cannot reproduce the address. On `GET .../onboarding` it is `null`.\n\nIt used to be non-null on every step at `action_required`, built from the step\nkey — and that URL is rejected by the page it addresses. `pending_action.link`\nis the field to use: it names the call that mints a URL that works.\n"
                },
                "link": {
                  "type": [
                    "object",
                    "null"
                  ],
                  "description": "WHAT THE HOSTED LINK FOR *THIS* STEP IS DOING (contract C3, 2026-09).\n\nExactly the object documented at `PendingAction.link` — `status`, `minted_at`,\n`expires_at`, `opened_at`, `requested_by`, `mint` (null while a link is `live`\nor `opened`), `ttl_seconds`, `reusable` — published on EVERY step a person can\nbe sent to, not only on the one `blocked_on` names. `null` on the steps that\nare ours, where there is nobody to send.\n\nIt was published only inside `pending_action`, which exists on one step at a\ntime, so an agent that had minted links for two steps could read the state of\none of them. That is the exact question this contract exists to answer, because\nminting again REVOKES the URL somebody may already be holding.\n"
                },
                "pending_action": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/PendingAction"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "The §7.4 invariant, in the field that can actually keep it: onboarding never\nasks for a human without telling them what to DO and how to reach the page.\nNon-null on every `customer` step that owes an action — the one at\n`action_required` and the one `blocked_on` names — and `null` on every step\nthat owes none, and on the steps that are ours: instructions to a person about\nwork we run unattended would be worse than silence.\n"
                },
                "completed_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "evidence_source": {
                  "type": "string",
                  "enum": [
                    "sii_probe",
                    "own_record",
                    "no_producer",
                    "attested"
                  ],
                  "description": "How this step's completion becomes known (#395), so a caller never has to guess\nwhether anything is actually watching.\n\n`sii_probe` — we re-read the SII and the step advances on what it says.\n`own_record` — the act lands in our own tables, so we see it as it happens.\n`no_producer` — NOTHING IN THIS BUILD OBSERVES IT (`habilitacion` today, the\nboleta habilitación at `certBolElectDteInternet`, an uncaptured SII SPA). Use\n`POST .../onboarding/steps/{step}/attest` — never a plain `recheck`, which\ncannot confirm what nothing observes.\n`attested` — a `no_producer` step that reached `completed` through exactly that\natestación. The only way such a step can be `completed` in this build.\n"
                },
                "execution": {
                  "type": "string",
                  "enum": [
                    "automatic",
                    "operator"
                  ],
                  "description": "HOW A STEP OF OURS ACTUALLY RUNS (2026-09, #487; derived per deployment\nsince 2026-09-07, #556).\n\n`actor: facturia` says who owns the step and nothing about whether anybody is\nwatching. Seven of ours reach the SII only when a fence is open — the\npostulación, the `intercambio` uploads, the `muestras_impresas` filing, the\nDeclaración de Cumplimiento, the boleta `habilitacion`, and the «Avanzar\nSiguiente Paso» that closes `set_de_pruebas` and `simulacion` (one fence, one\nact, both pasos) — and while a fence is shut, that step is a person of ours at\na keyboard. The first five published `in_progress` indistinguishably from a job\non a schedule; the stage-advance pair was worse and published `automatic`\noutright while a person clicked in a browser. Either way it is the difference\nbetween \"this finishes overnight\" and \"this finishes when somebody is at a\ndesk\".\n\n**THIS IS NOT A CONSTANT OF THE PRODUCT. It is read per deployment**, from the\nsame environment variable the executor reads, so the SAME step can answer\n`operator` on one deployment and `automatic` on another, and can change from\none day to the next without a new API version. Branch on the value you were\ngiven; never cache it as a fact about a step key.\n\n`automatic` on every step with no such fence, including the customer's, where\nit means \"nothing of OURS is pending on it\". The authority for WHICH fence\nbelongs to which step is the lock register (`worker/src/onboarding/locks.ts`),\nwhose rows name the step each fence holds; a test walks it against the catalog\nin both directions, so a fence that names a step nothing publishes as an\noperator's fails the build, and so does the reverse.\n"
                },
                "eta_business_days": {
                  "type": "integer",
                  "description": "This step's own p50, in business days (2026-09, #487). `estimated_ready_at` at\nthe top of the body is the sum over everything still to run; this is the one\nline of it, so a checklist can put a number beside the row a person is actually\nlooking at. A p50, not a promise.\n\n**A STEP READING `execution: operator` NEVER PUBLISHES LESS THAN 2** (2026-09-07,\n#556). The catalog's base numbers were decomposed from a queue — 0 means \"the\nSII answers this envelope in minutes\" — and none of them describes a person of\nours finding an hour for a sitting. So while a step waits on one, its p50 is\nfloored at two business days, and it drops back to the base the moment that\nstep's fence opens. Read it off the wire per step; it moves with `execution`.\n"
                },
                "next_poll_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time",
                  "description": "WHEN WE LOOK AT THE SII AGAIN FOR THIS STEP (2026-09-07, #563), or `null` when\nno read is scheduled.\n\nPresent on every step, and it is the field that makes an `in_progress` step\nhonest. A step whose last probe learnt nothing — the SII did not answer, or\nanswered \"granted, not yet applied\" — used to be indistinguishable on the wire\nfrom one that is about to finish, and an agent reading the checklist reported\n\"we are doing fine\" about a wait nobody had timed. Render it with\n`blocked_reason`: \"el SII no contestó; volvemos a mirar a las 13:47\".\n\nA SCHEDULE, not a promise: it is when the probe's own cadence falls due, and\nthe reconciler notices within about five minutes of it. `POST\n.../onboarding/steps/{step}/recheck` asks now instead of waiting for it.\n"
                },
                "blocked_reason": {
                  "type": "object",
                  "description": "Why this step is where it is, as structured data with a `code` and a Spanish\n`message`. On the steps only a representante legal may perform it also carries\na `packet` — the SII page to open, the empresa RUT, a checklist, and the\nreminder that the step needs THEIR certificate, not the one you custodied with\nus. Additional keys may appear; branch on `code`.\n\nON `failed`, `action_required` **AND `in_progress`** (2026-09-07, #563). This\nsaid \"failed or action_required\", and the third case is the one a caller most\nneeds: a step that is running publishes the LAST PROBE OUTCOME while that probe\ndid not settle it — `sii_unreachable` (no answer from the SII), `sii_propagation`\n(the SII shows the grant and has not applied it yet), `requirement_unmet` (its\nown user table refuses the empresa's sender; `details.key` names the permiso).\nNone of the three blocks the empresa, names it in `blocked_on` or asks for a\nperson: they carry `retriable: true` and a Spanish `hint`, and `next_poll_at`\nbeside them says when the next read is due. Absent while a running step has\nnothing to report.\n"
                }
              }
            }
          },
          "certifications": {
            "type": "array",
            "description": "Doc types certify separately; boletas certify in parallel with facturas.",
            "items": {
              "type": "object",
              "properties": {
                "tipo_dte": {
                  "$ref": "#/components/schemas/TipoDte"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "not_started",
                    "in_progress",
                    "certified",
                    "failed"
                  ]
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "iva_shape": {
            "$ref": "#/components/schemas/TaxpayerIvaShape"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PendingAction": {
        "type": "object",
        "description": "The handoff for an act that still needs a person — the OAuth-consent analogue of this\nAPI.\n\nWHY A PERSON, AND IT IS NOT WHAT THIS FIELD SAID UNTIL 2026-09-04. It said the SII\n\"requires the taxpayer or their legal representative to authenticate personally, with\nTHEIR OWN certificate\", and that the custodied one \"signs documents; it cannot\nauthenticate a person\". That is false, and it was the load-bearing sentence under three\nsurfaces. A Chilean CA issues certificates to NATURAL PERSONS: the one in the vault is\nthe representante legal's (mandato 1.3), and a TLS client-certificate session with the\nSII authenticates AS ITS HOLDER. Facturia ran an entire boleta certification with it,\nend to end, from code, on 2026-09-04.\n\nWhat remains is a LEGAL line rather than a technical one, and it is narrower. Mandato\nclausula 4.6 \"Actos reservados al Cliente\" reserves some acts to the customer; a few\nothers are decisions only they can take because of what the act COSTS — the month an\nempresa stops being a free-system user, the moment a 24-hour SII clock starts. Those\narrive as an `approval` block below: we perform the act with the custodied certificate,\nand a person authorises it on a hosted page of ours. The `sii_postulacion` step is\nexactly that shape now — Facturia files the postulacion, after the titular approves.\n\nEverything a consumer needs to relay the errand to a human is here, so no second call is\nneeded and no click path has to be improvised. Customer-facing strings are in **Spanish**\nbecause the person who performs the act is Chilean and is frequently not the account\nholder; field names are English like everything else.\n\nPresent on `steps[].pending_action` for any step that asks a human to act, hoisted to\n`onboarding.pending_action` for the step `blocked_on` names, and carried inside\n`error.details.pending_action` when a `/v1` write is refused because one is outstanding.\n`null` whenever the wait is ours or the SII's - which means wait, not act.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "pending_action"
          },
          "act": {
            "type": "string",
            "description": "The ACT, which is not always the step. One step can park on two different errands -\n`certificate` means \"buy a certificate from a CA\" with no certificate on file and\n\"grant «Enviar Doctos.» in Mantencion de usuarios\" when the holder lacks the\npermission, which is a different portal and possibly a different person. Branch on\nthis rather than on `step` when you care which.\n"
          },
          "step": {
            "type": "string",
            "description": "The onboarding step this handoff hangs off."
          },
          "empresa_id": {
            "type": "string"
          },
          "title": {
            "type": "string",
            "description": "Spanish, one line: what must happen."
          },
          "summary": {
            "type": "string",
            "description": "Spanish, for someone who has never heard of the SII."
          },
          "action_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "A LIVE hosted page, or `null`. The same value as the step's `action_url`.\n\nThe hosted pages are reached by a single-use capability token that is stored only as a\nSHA-256, so no read can reproduce one: this field is non-null only in the response that\nMINTED the link (`POST /v1/empresas/{id}/onboarding/links`, or `POST /v1/empresas/{id}/mandato`\nwith an empty body). Everywhere else it is `null` and `link` below says how to get one.\n\nIt used to be a URL built from the step key and never null - and that URL is REJECTED by\nthe page it addresses, which answers \"Esta pagina se abre desde el enlace que te\nmandamos\" without a `?token=`. Relaying it sent a representante legal to a page telling\nthem to ask somebody for a link. Do not reconstruct it; mint it.\n"
          },
          "link": {
            "type": "object",
            "description": "What the hosted link for this step IS DOING, and how to obtain one. Always present.\n\nThe refusal points at the endpoint that fixes it, the way RFC 9728's `resource_metadata`\npoints a `401` at the consent flow. Issue `mint` exactly as given; the response carries\nthe URL once.\n\n**`status` first, then `mint` (2026-09, #459).** `action_url` is `null` on every read by\nconstruction - the token is stored only as a SHA-256 - so an agent re-reading this body\nsaw a null URL beside a mint recipe and minted again. For `mandato` that REVOKES the link\nthe representante legal already has in their inbox; for any other step it retires a URL\nsomebody may be about to open. `mint` is therefore `null` exactly while `status` is\n`live` or `opened`: there is already a link, and minting would destroy it. Branch on\n`status`, and treat a `null` `mint` as \"you already have one\", never as an error.\n",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "none",
                  "live",
                  "opened",
                  "consumed",
                  "expired",
                  "revoked"
                ],
                "description": "- `none` - nobody has minted one. Mint.\n- `live` - minted, nobody has opened it. Re-send the URL you were given, or wait.\n- `opened` - somebody has the page open. Minting now breaks their tab.\n- `consumed` - somebody acted on it. The step moves on its own from here.\n- `expired` - the clock ran out. Mint a fresh one.\n- `revoked` - it was cancelled or superseded. Mint a fresh one.\n"
              },
              "minted_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "expires_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "opened_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "When somebody first opened the page. Never moves on a reload, and always `null` for the mandato, whose page records none."
              },
              "requested_by": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "Who asked for the link, AS THEY DESCRIBED THEMSELVES. Optional on the mint\n(`POST .../onboarding/links` and `POST .../mandato` both accept\n`requested_by: {kind, name}`), and rendered to the person who receives the link -\n\"Este enlace lo pidio Claude Desktop\" instead of \"lo genero una integracion\".\n\nIt is a CLAIM, never an authentication fact: the credential the mint actually\narrived on is recorded separately and is not published here.\n",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "agent",
                      "panel",
                      "api"
                    ]
                  },
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "mint": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "How to mint one - `null` while `status` is `live` or `opened`, because minting there\nREPLACES a link somebody is already holding.\n",
                "properties": {
                  "method": {
                    "type": "string",
                    "const": "POST"
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Absolute, so this object alone is enough to issue the request."
                  },
                  "body": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "`{ \"step\": \"...\" }` for the hosted links endpoint; `{}` for the mandato."
                  }
                }
              },
              "ttl_seconds": {
                "type": "integer",
                "description": "How long the minted link lives. 7 days for the mandato, 30 for the rest."
              },
              "reusable": {
                "type": "boolean",
                "description": "`false` for the mandato - its token is single use, because it produces a legal\ninstrument and a second click must be told \"ya se firmo\" rather than offered a\nsecond signature. `true` for every other step: those produce no instrument of ours,\nso the link is a durable address for a checklist a person may re-read before acting.\n"
              }
            }
          },
          "agent_actions": {
            "type": "array",
            "description": "What YOU can do here, as opposed to the human. Always present; usually empty,\nbecause these acts happen at the SII under a person's own certificate and a list\nfull of hopeful-looking calls would be worse than none.\n\nThe `cumplimiento` handoff is the one that carries a real entry: `POST\n.../onboarding/go_live` with an empty body, which mints the approval link for the\ntitular. That call used to appear only as Spanish prose inside a\n`prerequisites[].label` - which this object tells you to relay VERBATIM - so agents\nread it out to a representante legal who has no API key.\n",
            "items": {
              "type": "object",
              "properties": {
                "method": {
                  "type": "string",
                  "const": "POST"
                },
                "url": {
                  "type": "string",
                  "format": "uri",
                  "description": "Absolute; issue it as given."
                },
                "body": {
                  "type": "object",
                  "additionalProperties": true
                },
                "label": {
                  "type": "string",
                  "description": "Spanish, one line: what this call does."
                },
                "when": {
                  "type": "string",
                  "description": "Spanish, one line: when to make it, relative to the human's act."
                }
              }
            }
          },
          "sii_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "The SII's own page, when we can name one that will not 404. `null` when we were not\ntold which ambiente the empresa runs in - maullin is certification and palena is\nproduction, and a representante legal sent to the wrong one either bounces or\nrehearses the real thing, so we say nothing rather than guess.\n"
          },
          "performed_by": {
            "type": "object",
            "description": "WHO, specifically. Never \"the customer\".",
            "properties": {
              "role": {
                "type": "string",
                "enum": [
                  "representante_legal",
                  "usuario_administrador",
                  "titular_certificado",
                  "titular_cuenta"
                ]
              },
              "label": {
                "type": "string",
                "description": "Spanish. The person, not the party."
              },
              "why_not_delegable": {
                "type": "string",
                "description": "Spanish, one sentence. Relay it: \"the law requires you personally\" persuades a\nreluctant signatory far better than \"the software cannot do this\".\n"
              },
              "legal_basis": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "The citations that sentence stands on. Verbatim and checkable."
              }
            }
          },
          "prerequisites": {
            "type": "array",
            "description": "What to have in hand BEFORE opening the page.",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                }
              }
            }
          },
          "instructions": {
            "type": "array",
            "description": "Ordered and concrete, contiguous from 1. Relay them VERBATIM - do not paraphrase and\ndo not add steps. See `evidence` for how much these can be trusted.\n",
            "items": {
              "type": "object",
              "properties": {
                "order": {
                  "type": "integer"
                },
                "text": {
                  "type": "string"
                }
              }
            }
          },
          "consequences": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Spanish. What performing the act sets in motion, where that is not obvious and\ngetting it wrong is expensive - the declaracion de cumplimiento starts the empresa's\nInformacion Electronica de Compras y Ventas obligation from the month it is made.\nEmpty for acts with no such consequence.\n"
          },
          "verification": {
            "type": "object",
            "description": "How we find out it happened. Never \"the customer tells us\".",
            "properties": {
              "method": {
                "type": "string",
                "enum": [
                  "sii_probe",
                  "own_record",
                  "no_producer"
                ],
                "description": "`sii_probe` - we re-read the SII and the step advances on what it says.\n`own_record` - the act lands in our own tables, so we see it as it happens.\n`no_producer` - NOTHING IN THIS BUILD OBSERVES IT. Named rather than hidden: a\nstep whose completion nobody produces would otherwise sit `action_required`\nforever while everyone assumes something is watching. Do not tell a user we are\nwatching for one of these.\n"
              },
              "description": {
                "type": "string"
              },
              "recheck_url": {
                "type": [
                  "string",
                  "null"
                ],
                "deprecated": true,
                "description": "The recheck endpoint for this step, as `POST <path>` — a method glued to a relative\npath a caller has to parse apart and prefix a host onto. **Deprecated — use\n`recheck` instead**, which names the identical endpoint as `{method,url}`, absolute\n(#419). Null when there is nothing to ask. Kept, and still populated, for existing\ncallers.\n"
              },
              "recheck": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "The same endpoint as `recheck_url`, structured (#419, contract (d)) — the shape\n`link.mint` already ships. `null` under the exact same condition as `recheck_url`:\nnothing to ask (`method` above is `no_producer`).\n",
                "properties": {
                  "method": {
                    "type": "string",
                    "const": "POST"
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Absolute, so this object alone is enough to issue the request."
                  }
                }
              },
              "sii_latency": {
                "type": "string",
                "description": "Spanish. Roughly how long the SII takes to show it."
              }
            }
          },
          "estimated_minutes": {
            "type": "integer",
            "description": "The human's time, at p50."
          },
          "next": {
            "type": "string",
            "description": "Spanish. What happens after, and whether they must tell us."
          },
          "evidence": {
            "type": "string",
            "enum": [
              "captured",
              "documented",
              "entry_point_only"
            ],
            "description": "How well-evidenced the click path is, published because a confidently wrong\ninstruction is worse than an honest vague one.\n\n`captured` - we have driven this exact chain and the pages are fixtures in our\nrepository; the instructions name real screens. `documented` - the SII's own\ndocumentation names the path and we have not walked it; you get the menu route, not\na field-by-field script. `entry_point_only` - we can name the door and nothing behind\nit.\n"
          },
          "caveat": {
            "type": [
              "string",
              "null"
            ],
            "description": "Spanish. Non-null whenever `evidence` is not `captured`, saying exactly what we are\nunsure about. RELAY IT. Dropping it turns \"the exact screen may differ\" into a\npromise we cannot keep.\n"
          },
          "copy": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "string"
            },
            "description": "Copy by phase, for the one act whose hosted page and dashboard wizard used to write\ntheir own prose and drifted apart over who declares (#393). `null` for every act\nexcept `cumplimiento`, which publishes `before_authorization`, `after_authorization`\nand `after_filed`.\n\nTHIS IS THE SINGLE AUTHOR. Both surfaces render this rather than keeping their own\nversion — which is exactly how one used to say «cuando el SII abra el trámite lo\nhacemos nosotros» while this very object, right beside it, said the representante\nlegal declares in person (cláusula 4.6 b). It says the truth of this build: Facturia\ndoes not declare (`ALLOW_LIVE_CUMPLIMIENTO_WRITES` is `false`, structurally); once\nthe titular approves the month, the copy says to declare in the SII with the packet —\nnever that Facturia will do it.\n"
          }
        }
      },
      "OnboardingRecheck": {
        "type": "object",
        "description": "The answer to a recheck. It records that you asked and says what we queued; it verifies\nnothing by itself.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "onboarding_recheck"
          },
          "empresa_id": {
            "type": "string"
          },
          "step": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "in_progress",
              "action_required",
              "completed",
              "failed",
              "skipped"
            ],
            "description": "The step's status as it stood when you called, read BEFORE anything was enqueued and\nreturned UNCHANGED. Reading back the same status you posted against is the endpoint\nworking correctly.\n"
          },
          "accepted": {
            "type": "boolean",
            "description": "The claim was recorded and the producers queued."
          },
          "verified": {
            "type": "boolean",
            "description": "Always `false`. Verification is what the producer does, later, against the SII. This\nfield exists so no caller can mistake acceptance for confirmation.\n"
          },
          "evidence_source": {
            "type": "string",
            "enum": [
              "sii_probe",
              "own_record",
              "no_producer",
              "sandbox"
            ],
            "description": "How this step can be confirmed. See `PendingAction.verification.method`."
          },
          "contact": {
            "type": [
              "object",
              "null"
            ],
            "description": "WHERE TO GO when `evidence_source: 'no_producer'` means this very call could not\nconfirm anything (#395) — `null` for every other `evidence_source`. Named as data\nrather than left in `message`'s prose, so a caller holding only this response can\nstill act on it.\n",
            "properties": {
              "channel": {
                "type": "string",
                "enum": [
                  "attest"
                ]
              },
              "api": {
                "type": "string",
                "deprecated": true,
                "description": "`POST /v1/empresas/{id}/onboarding/steps/{step}/attest` for this step.\n\nDEPRECATED (2026-09, #497): a method glued to the front of a relative path, so a\ncaller has to split it and prefix a host it knows separately - while\n`pending_action.link.mint` two fields away needs neither. Use `call`. Still\npopulated, unchanged.\n"
              },
              "call": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ApiCall"
                  },
                  {
                    "description": "The same endpoint as `api`, in the one shape"
                  }
                ]
              }
            }
          },
          "queued": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Job types this call enqueued."
          },
          "already_queued": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Job types already scheduled for this five-minute window. Not a failure - \"we are\nalready about to look\" is a true answer to \"please look\".\n"
          },
          "message": {
            "type": "string",
            "description": "Spanish, for the person who made the claim."
          },
          "pending_action": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PendingAction"
              },
              {
                "type": "null"
              }
            ],
            "description": "The handoff again, so a caller who got the errand wrong can re-read it."
          },
          "onboarding_url": {
            "type": "string",
            "deprecated": true,
            "description": "Relative. DEPRECATED (2026-09, #497) in favour of `next`, which is the same read as\n`{method, url}` absolute - no method to split off the front, no host to prefix by\nhand. Still populated, unchanged.\n"
          },
          "next": {
            "$ref": "#/components/schemas/ApiCall"
          },
          "requested_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "OnboardingAttestation": {
        "type": "object",
        "description": "The answer to an atestación (#395). Unlike a recheck, this call DOES write a fact — one\n`sii_onboarding_evidence` row — for the one shape of step (`evidence_source: no_producer`)\nthis build cannot otherwise ever observe.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "onboarding_attestation"
          },
          "empresa_id": {
            "type": "string"
          },
          "step": {
            "type": "string"
          },
          "attested_by": {
            "type": "string",
            "description": "Who told us this happened — a name, or the representante legal's RUT."
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          },
          "attested_at": {
            "type": "string",
            "format": "date-time"
          },
          "accepted": {
            "type": "boolean"
          },
          "evidence_source": {
            "type": "string",
            "enum": [
              "attested"
            ]
          },
          "queued": {
            "type": "boolean",
            "description": "Whether the fold tick was enqueued. `false` only on an insert failure; the hourly sweep still catches it."
          },
          "message": {
            "type": "string",
            "description": "Spanish."
          },
          "onboarding_url": {
            "type": "string",
            "deprecated": true,
            "description": "Relative. DEPRECATED (2026-09, #497) in favour of `next`, which is the same read as\n`{method, url}` absolute - no method to split off the front, no host to prefix by\nhand. Still populated, unchanged.\n"
          },
          "next": {
            "$ref": "#/components/schemas/ApiCall"
          }
        }
      },
      "ApiCall": {
        "type": "object",
        "description": "ONE SHAPE FOR \"THE NEXT CALL\" (#419, #497). A method plus an ABSOLUTE url, optionally\nwith the body and headers to send, so an object carrying one is enough to issue the\nrequest — no method parsed off the front of a string, no host prefixed by hand, no\nrelative path resolved against a base the caller has to know.\n\nFive different spellings of this idea shipped before it was named. Where an older field\npublished one of them (`capabilities.next.api`, `contact.api`, `onboarding_url`), the\nold field is KEPT and populated and an `ApiCall` sibling is added beside it — additive,\nper rule #419 — and the prose on each says which one to branch on.\n",
        "required": [
          "method",
          "url"
        ],
        "properties": {
          "method": {
            "type": "string",
            "enum": [
              "GET",
              "POST",
              "PATCH",
              "PUT",
              "DELETE"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Absolute. Never a path."
          },
          "body": {
            "type": "object",
            "additionalProperties": true,
            "description": "The exact body to send, when the call takes one."
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "OnboardingStepApproval": {
        "type": "object",
        "description": "One decision by a human authorizing (or refusing) an onboarding act Facturia then\nperforms on their behalf (#452, #453). Append-only: a caller reading back takes the\nnewest row for the act and branches on `decision`, because a rejection is a decision\nand not an absence.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "onboarding_step_approval"
          },
          "empresa_id": {
            "type": "string"
          },
          "step": {
            "type": "string"
          },
          "act": {
            "type": "string",
            "description": "What was authorized, as `pending_action.approval.act` named it at the time."
          },
          "decision": {
            "type": "string",
            "enum": [
              "approve",
              "reject"
            ]
          },
          "role": {
            "type": "string",
            "enum": [
              "titular_cuenta",
              "rep_legal"
            ]
          },
          "decided_via": {
            "type": "string",
            "enum": [
              "hosted",
              "session"
            ],
            "description": "`hosted` — a person holding the enlace de aprobacion pressed the button, the form\nADR-0012 §1 calls canonical. `session` — the decision was stated through an\nauthenticated surface.\n"
          },
          "decided_at": {
            "type": "string",
            "format": "date-time"
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          },
          "queued": {
            "type": "boolean",
            "description": "Whether the fold tick was enqueued. Always `false` for a rejection — there is nothing to fold."
          },
          "onboarding_url": {
            "type": "string"
          },
          "next": {
            "$ref": "#/components/schemas/ApiCall"
          }
        }
      },
      "AutomationSettings": {
        "type": "object",
        "description": "Every automation defaults to OFF except `auto_request_folios`.",
        "properties": {
          "object": {
            "type": "string",
            "const": "automation_settings"
          },
          "empresa_id": {
            "type": "string"
          },
          "auto_emit_46": {
            "type": "boolean",
            "default": false
          },
          "auto_emit_46_max_amount": {
            "$ref": "#/components/schemas/Clp"
          },
          "auto_file_f29": {
            "type": "boolean",
            "default": false
          },
          "auto_file_f29_day": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "default": 18
          },
          "auto_file_f29_max_payable": {
            "$ref": "#/components/schemas/Clp",
            "default": 0,
            "description": "Default 0 — out of the box only a zero or refund position files itself, and any\nmonth you actually owe IVA waits for a human. Raise deliberately, per empresa.\n"
          },
          "auto_request_folios": {
            "type": "boolean",
            "default": true
          },
          "auto_request_folios_threshold": {
            "type": "integer",
            "default": 50
          },
          "auto_accept_received": {
            "type": "boolean",
            "default": false
          },
          "auto_accept_received_after_days": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 7
          },
          "require_confirmation": {
            "type": "boolean",
            "default": false,
            "description": "When true, money-moving actions initiated from an agent surface — emitting a document,\nfiling an F29, registering a reclamo through the MCP server's tools — require a\ndraft → confirm two-step instead of executing on the first call. It exists primarily\nfor the MCP surface (see `mcp-server-spec.md`); the REST API described here is\nunaffected, and the flag lives on this resource so one settings object governs the\nempresa on every surface.\n"
          },
          "rcv_sync_frequency": {
            "type": "string",
            "enum": [
              "hourly",
              "daily",
              "weekly",
              "manual"
            ],
            "default": "daily"
          },
          "honor_estado_contable_override": {
            "type": "boolean",
            "default": false,
            "description": "Whether the F29 propuesta obeys the LOCAL `estado_contable_override` set through\n`POST /tax/purchases/{entry_id}/estado_contable` instead of the `estado_contable` the\nSII's own register supplied.\n\nOff by default, and the direction of that default is the point: only `REGISTRO`\npurchases carry a crédito fiscal, so a local `PENDIENTE` → `REGISTRO` flip claims IVA\nthe SII does not agree exists — and the SII's value is the one an audit reads. Turned\non, every affected propuesta carries an explicit observation naming the documents, and\nevery write is recorded on the document's event log.\n"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Branding": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "branding"
          },
          "logo_url": {
            "type": "string",
            "format": "uri"
          },
          "default_pdf_format": {
            "type": "string",
            "enum": [
              "carta"
            ],
            "default": "carta",
            "description": "`carta` is the only legal value — `80mm` was removed in wave 9 along with the `format`\nparameter, and no thermal layout exists. `PATCH` refuses any other value with\n`400 invalid_field` (ticket #18 wave 10). There is nothing further for it to select: it\nalready names the only layout this API renders, so validating it is what \"applying\" it\nmeans.\n"
          },
          "cedible_by_default": {
            "type": "boolean",
            "default": false,
            "description": "Applies to facturas — tipo 33 and 34 (Ley 19.983; not \"tipo 33 only\", corrected in\nticket #18 wave 10). Consumed by `GET /documents/{document_id}/pdf`'s default-copy\nresolution: a per-document `pdf.cedible` (§9.3) overrides it; an explicit `?copy=`\noverrides both. See that endpoint's `copy` parameter for the full precedence.\n"
          },
          "footer_text": {
            "type": "string"
          }
        }
      },
      "Caf": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "caf"
          },
          "id": {
            "type": "string"
          },
          "tipo_dte": {
            "$ref": "#/components/schemas/TipoDte"
          },
          "folio_from": {
            "type": "integer"
          },
          "folio_to": {
            "type": "integer"
          },
          "active": {
            "type": "boolean"
          },
          "source": {
            "type": "string",
            "enum": [
              "sii_request",
              "upload"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "FolioRange": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "folio_range"
          },
          "tipo_dte": {
            "$ref": "#/components/schemas/TipoDte"
          },
          "next_folio": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The folio the NEXT emission takes — which, once the current range is spent, is the first folio of the range queued behind it, not the out-of-range number the counter is parked on. Null when no emission of this tipo could succeed right now, and null on the `sfg_managed` lane, where the SII assigns a folio per emission."
          },
          "available": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Folios the next emissions can actually draw, ACROSS EVERY RANGE this empresa holds for the tipo — the current one plus each range queued behind it, since `allocate_dte_folio` rolls onto them rather than failing. Never derive a shortage from `next_folio` and a range end: an empresa drawing folio 25 of 6–25 while holding 26–45 has 21 available, not 1. Null (never zero) on `sfg_managed`."
          },
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "low",
              "exhausted",
              "sfg_managed"
            ]
          },
          "stock": {
            "type": "object",
            "description": "Where `available` comes from: the ranges the allocator will consume, in the order it will consume them. Present on the mercado lane only.",
            "properties": {
              "chain": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "caf_id": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "from": {
                      "type": "integer"
                    },
                    "to": {
                      "type": "integer"
                    },
                    "remaining": {
                      "type": "integer"
                    },
                    "expired": {
                      "type": "boolean",
                      "description": "Past its vigencia for a tipo Res. Ex. SII 58/2017 §1° binds. Contributes nothing and ends the walk — the emission path refuses it."
                    }
                  }
                }
              },
              "stops_at_expired_range": {
                "type": "boolean",
                "description": "The walk ended on a lapsed range rather than on \"nothing more was granted\". Not the same as `available == 0`: a chain can carry usable folios now and still stop at an expired range behind them."
              }
            }
          },
          "ranges": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "caf_id": {
                  "type": "string"
                },
                "from": {
                  "type": "integer"
                },
                "to": {
                  "type": "integer"
                },
                "expires_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                }
              }
            }
          }
        }
      },
      "DocumentStatus": {
        "type": "string",
        "enum": [
          "queued",
          "sent",
          "accepted",
          "reparo",
          "rejected",
          "failed",
          "annulled"
        ],
        "description": "queued → sent → accepted | reparo | rejected; failed is retriable; annulled follows a\nnota de crédito with code 1. See the estado mapping table in the prose spec for how each\nSII signal (DOK/DNK/FAU/FNA/FAN/EMP/RCT/EPR/RPR…) lands here.\n"
      },
      "DocumentItem": {
        "type": "object",
        "required": [
          "description",
          "quantity",
          "unit_price"
        ],
        "properties": {
          "description": {
            "type": "string",
            "maxLength": 80
          },
          "detail": {
            "type": "string",
            "maxLength": 1000
          },
          "quantity": {
            "type": "number"
          },
          "unit": {
            "type": "string",
            "examples": [
              "UN",
              "KG",
              "HR"
            ]
          },
          "unit_price": {
            "type": "number",
            "description": "Up to 4 decimals. Net or gross depending on `prices_include_tax` — but on a factura (33/34/56/61/46) the document's own `<PrcItem>`/`<MontoItem>` is always NET (`DTE_v10.xsd`: `MontoItem` is net unless `MntBruto` is set, which this API never sends), so `prices_include_tax: true` there is normalized DOWN at the door: the echoed `items`, the PDF and `<PrcItem>` all show the NET price (`11990` → `10075.6303`), and the total may land a peso off the quoted gross figure because every downstream amount is computed from the rounded net price. On a boleta (39/41), whose `MontoItem` is gross by schema, the price stays gross regardless of `prices_include_tax`. On a guía de despacho (52) emitted through `emission_channel=sfg` it must be greater than 0 on every line — the SII portal validates its guía form with its factura validations and refuses a zero line whatever the traslado kind (`422 sfg_guia_zero_amount`). `emission_channel=mercado` accepts a zero-amount guía."
          },
          "discount_percent": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Per-line discount, emitted as the SII's `<DescuentoPct>`/`<DescuentoMonto>` — MERCADO CHANNEL ONLY. On `emission_channel=sfg` any value greater than 0 is refused with `422 line_discount_not_available_for_channel`: the SII portal's emission form has no per-line discount field we have ever captured (the captured form comments its discount inputs out), so accepting it would quote you a discounted total and file the undiscounted one. Fold the discount into `unit_price`, or emit through `emission_channel=mercado`. `global_discounts` is refused on that channel for the same reason. A `0` is accepted on both channels — it is exactly what the portal would file."
          },
          "amount": {
            "$ref": "#/components/schemas/Clp"
          },
          "item_code": {
            "type": "object",
            "description": "Emitted as the SII's `<CdgItem><TpoCodigo>/<VlrCodigo>`. Both `type` and `value` are required together; a half-filled pair is dropped rather than emitted as an empty element.",
            "properties": {
              "type": {
                "type": "string",
                "examples": [
                  "INT1",
                  "EAN"
                ]
              },
              "value": {
                "type": "string"
              }
            }
          }
        }
      },
      "DocumentReference": {
        "type": "object",
        "required": [
          "tipo_dte",
          "folio"
        ],
        "properties": {
          "tipo_dte": {
            "type": "integer",
            "description": "The referenced document's SII type code: a DTE type (33/34/39/41/46/52/56/61) or one of\nthe SII's non-DTE reference codes in the 800 range (e.g. 801 orden de compra, 802 nota de pedido).\n"
          },
          "folio": {
            "type": [
              "string",
              "integer"
            ],
            "description": "An integer is coerced to its decimal string, since `<FolioRef>` is `xs:string` in the SII's schema (a tipo 801 orden de compra folio like `OC-99213` is never a number). Sent either way, capped at 18 characters."
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "Defaults to the document's own `issue_date` when omitted."
          },
          "code": {
            "type": [
              "integer",
              "null"
            ],
            "enum": [
              1,
              2,
              3,
              null
            ],
            "description": "1 anula · 2 corrige texto · 3 corrige montos. Null for non-DTE references."
          },
          "reason": {
            "type": "string",
            "maxLength": 90
          },
          "force": {
            "type": "boolean",
            "default": false,
            "description": "Reference a document that was emitted OUTSIDE Facturia. By default a reference to a DTE type with a numeric folio is checked against this empresa's own emitted documents and refused with `422 reference_not_found` (or `reference_not_accepted`) when the document is missing or the SII has not accepted it — a typo there costs the referencing document's own folio. Set `force: true` per reference for a factura issued by a previous provider, which is legitimate and unknowable to us. Never a default, and not a DTE field: it is consumed at the door."
          }
        }
      },
      "DocumentCreateRequest": {
        "type": "object",
        "required": [
          "tipo_dte",
          "items"
        ],
        "description": "`tipo_dte` and `items` are required UNLESS `preview_id` is sent, in which case the stored\nrequest is replayed and only `external_id` and `metadata` may accompany it.\n",
        "properties": {
          "preview_id": {
            "type": "string",
            "examples": [
              "prev_01K2R9M3D8V5QW7N"
            ],
            "description": "Emit a previously computed preview (`POST /documents/previews`). The stored request is\nreplayed through the ordinary create path, so nothing about the emission differs from a\ndirect one. Only `external_id` and `metadata` may be supplied alongside it — nothing\nthat could change an amount, a receptor or a date. Emitting the same preview twice\nreturns the original document with `200` rather than a second `201`.\n"
          },
          "tipo_dte": {
            "$ref": "#/components/schemas/TipoDte"
          },
          "external_id": {
            "type": "string",
            "description": "Your own identifier. Unique per empresa; a duplicate returns `409 external_id_exists`."
          },
          "issue_date": {
            "type": "string",
            "format": "date",
            "description": "Defaults to today in America/Santiago."
          },
          "prices_include_tax": {
            "type": "boolean",
            "description": "Defaults to true for 39/41 and false for every other tipo."
          },
          "receptor": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Party"
              }
            ],
            "description": "Who the document is issued to. `giro` here is the SII's `GiroRecep`, capped at **40 characters** — tighter than the emisor's own `GiroEmis` (80), so the same name of trade can be legal on one side of a document and illegal on the other. `legal_name` is 100 and `address` is 70/20/20. Over-long values are refused with `400 field_too_long` before a folio is allocated, and never truncated: shortening a razón social or a name of trade would change what the document says about the parties. `address` must be an object; a bare string is `400 invalid_field`, because there is no way to split one into the three components the SII asks for and dropping it silently would emit a document with no address at all. AND THE ADDRESS IS NOT OPTIONAL on 33, 34, 46, 52, 56 and 61: the SII accepts the ENVELOPE and then REJECTS the document whose `<Receptor>` carries no `GiroRecep`/`DirRecep`/`CmnaRecep`, with the folio already spent. So `giro`, `address.street` and `address.comuna` are resolved from this empresa's own earlier documents to the same RUT and, failing that, refused with `400 receptor_incomplete` before a folio is allocated. Three exceptions: a tipo 52 needs no `giro` (its receptor is often the emisor itself, whose own giro frequently exceeds 40); `emission_channel=sfg` also needs `address.city`, which the SII portal's own form demands; and 39/41 and a foreign 46 (`foreign: true`, or `55555555-5`) need none of them."
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/DocumentItem"
            }
          },
          "global_discounts": {
            "type": "array",
            "maxItems": 1,
            "description": "The engine emits a single global discount line — the SII's `<DscRcgGlobal>` is one hard-coded `NroLinDR>1` — so a second entry is refused with `400 invalid_field` before a folio is allocated, rather than dropped. MERCADO CHANNEL ONLY: on `emission_channel=sfg` it is refused with `422 global_discount_not_available_for_channel`, because the SII portal's emission form has no discount field at all — its `EFXP_PCT_DESC`/`EFXP_MNT_DESC` inputs are commented out in the captured form, and `items[].discount_percent` is refused there for the same reason. Fold the discount into `items[].unit_price` on that channel.",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "percent",
                    "amount"
                  ]
                },
                "value": {
                  "type": "number"
                },
                "applies_to": {
                  "type": "string",
                  "enum": [
                    "afecto",
                    "exento"
                  ]
                },
                "reason": {
                  "type": "string"
                }
              }
            }
          },
          "references": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DocumentReference"
            }
          },
          "dispatch": {
            "type": "object",
            "description": "Required for tipo 52, and its `transfer_type` / `dispatch_type` are refused on any other tipo. Both emission channels emit a guía; they differ in which fields they can express. `destination` and `dispatch_type` are accepted on `emission_channel=mercado` (the engine writes them into the DTE's `IdDoc/TipoDespacho` and Transporte block) and refused on `emission_channel=sfg` with `422 dispatch_not_available_for_channel`, because the SII portal's guía form has nowhere to put them: it carries a single address — the receptor's, so put a differing one in `receptor.address` — and its only traslado select is the kind of movement, with no TipoDespacho input at all. The two channels also differ on amounts: `sfg` refuses a guía with any line priced at or below 0, or a zero total, with `422 sfg_guia_zero_amount` — the portal validates the guía with its factura validations, whatever the traslado kind — while `mercado` accepts one.",
            "required": [
              "transfer_type"
            ],
            "properties": {
              "transfer_type": {
                "type": "string",
                "description": "The SII's IndTraslado — what the movement IS. Required; there is no default, because the SII's own form defaults to \"operación constituye venta\".",
                "enum": [
                  "venta",
                  "venta_por_efectuar",
                  "consignacion",
                  "entrega_gratuita",
                  "traslado_interno",
                  "otros",
                  "devolucion",
                  "traslado_exportacion",
                  "venta_exportacion"
                ]
              },
              "dispatch_type": {
                "type": "string",
                "enum": [
                  "receiver_account",
                  "issuer_to_customer_site",
                  "issuer_to_other_site"
                ],
                "description": "The SII's TipoDespacho — on whose account the despacho moves, and to whose premises. Optional: unlike `transfer_type` it changes nothing about what the document means, so a guía that omits it is complete. `receiver_account` = 1, despacho por cuenta del receptor del documento (the customer; the seller when the document is a factura de compra); `issuer_to_customer_site` = 2, por cuenta del emisor a instalaciones del cliente; `issuer_to_other_site` = 3, por cuenta del emisor a otras instalaciones (entrega en obra, bodega de un tercero). Accepted on `emission_channel=mercado`; refused on `sfg`, whose form has no such input. This field previously published `emisor_propio`/`emisor_arrendado`/`receptor`, which named whose vehicle moved the goods — a different question, refused on every channel and never written into a document; those three values are now `400 invalid_field`. Name the vehicle's owner with `carrier`."
              },
              "requester_rut": {
                "$ref": "#/components/schemas/Rut"
              },
              "carrier": {
                "type": "object",
                "properties": {
                  "rut": {
                    "$ref": "#/components/schemas/Rut"
                  },
                  "name": {
                    "type": "string"
                  },
                  "plate": {
                    "type": "string",
                    "maxLength": 8
                  },
                  "driver_rut": {
                    "$ref": "#/components/schemas/Rut"
                  },
                  "driver_name": {
                    "type": "string",
                    "maxLength": 30
                  }
                }
              },
              "destination": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Address"
                  }
                ],
                "description": "Delivery address, when it differs from `receptor.address`. Accepted on `emission_channel=mercado`, where the engine writes it into the DTE's Transporte block as DirDest/CmnaDest/CiudadDest; refused on `emission_channel=sfg`. The SII caps these at 70/20/20 characters and longer values are refused with `400 field_too_long` rather than truncated."
              }
            }
          },
          "withholding": {
            "type": "object",
            "description": "Tipo 46 — you are the withholding agent.",
            "properties": {
              "iva": {
                "type": "string",
                "enum": [
                  "total",
                  "partial"
                ],
                "default": "total",
                "description": "Only `total` — the whole IVA retained, the SII's ImpAdicDTE 15 «IVA Retenido Total» — is emittable today. `partial` is refused with `422 withholding_not_supported`: the engine has no way to state a partial rate yet."
              },
              "rate": {
                "type": "number",
                "description": "Only `19` is accepted. A total IVA retención is retained at 19% and the document has nowhere to state a different one; any other value is refused with `422 withholding_not_supported`."
              }
            }
          },
          "payment": {
            "type": "object",
            "properties": {
              "terms": {
                "type": "string",
                "enum": [
                  "contado",
                  "credito",
                  "sin_costo"
                ]
              },
              "due_date": {
                "type": "string",
                "format": "date"
              },
              "method": {
                "type": "string"
              }
            }
          },
          "totals": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Totals"
              },
              {
                "description": "Optional assertion. When present it must match our computation exactly,\nor the request fails with `422 totals_mismatch` and nothing is emitted.\n"
              }
            ]
          },
          "pdf": {
            "type": "object",
            "properties": {
              "cedible": {
                "type": "boolean",
                "description": "This document's own default copy for `GET /documents/{document_id}/pdf` when a read\ncarries no `?copy=` of its own — `true` defaults to the cedible copy, `false`\nexplicitly defaults to the original (overriding the empresa's\n`branding.cedible_by_default`, §7.6). An explicit `?copy=` on the read always wins\nregardless of this field. `true` on a tipo outside 33/34 is `422\ncedible_not_available` — refused before a folio is spent, not silently ignored.\n"
              },
              "public": {
                "type": "boolean",
                "description": "Also mint a signed, unguessable, expiring URL scoped to this one artifact, openable\nwithout a key. It comes back as `public_links.pdf_public_url` on THIS response and\nnowhere else — the secret half is stored hashed, so no later read can reproduce it.\n"
              },
              "link_expires_in_days": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 1,
                "maximum": 365,
                "default": 90,
                "description": "Null means the link does not expire."
              },
              "link_expires_in_seconds": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "The same lifetime in seconds, clamped to 30s–365d. Wins when both are sent."
              }
            }
          },
          "xml": {
            "type": "object",
            "description": "The XML artifact shares the PDF's public-link mechanism on identical terms — it is the\nartifact the receptor's accounting system actually ingests, so gating it behind a key\nwould force integrators to proxy a file we have already generated.\n",
            "properties": {
              "public": {
                "type": "boolean",
                "description": "Also mint a public link serving the signed bytes exactly as sent to the SII\n(`application/xml; charset=ISO-8859-1`), byte-identical to the authenticated route.\nIt comes back as `public_links.xml_public_url` on THIS response and nowhere else.\n"
              },
              "link_expires_in_days": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 1,
                "maximum": 365,
                "default": 90
              },
              "link_expires_in_seconds": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "The same lifetime in seconds, clamped to 30s–365d. Wins when both are sent."
              }
            }
          },
          "notes": {
            "type": "string"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Up to 20 keys, ≤500 chars each. Stored and returned untouched; never sent to the SII."
          },
          "test": {
            "type": "object",
            "description": "Test mode only. Sending this with a live key fails with `400 test_field_in_live_mode`.",
            "properties": {
              "outcome": {
                "type": "string",
                "enum": [
                  "accepted",
                  "reparo",
                  "rejected",
                  "failed",
                  "stuck"
                ],
                "default": "accepted"
              },
              "reason_code": {
                "type": "string"
              },
              "settle_after_ms": {
                "type": "integer",
                "minimum": 0,
                "maximum": 60000
              }
            }
          }
        }
      },
      "DocumentPreview": {
        "type": "object",
        "description": "A priced, fully validated document that was not emitted. Returned by\n`POST /documents/previews` and readable for an hour.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "document_preview"
          },
          "id": {
            "type": "string",
            "examples": [
              "prev_01K2R9M3D8V5QW7N"
            ]
          },
          "preview_id": {
            "type": "string",
            "description": "The same value as `id`, published under the name `POST /documents` takes it by. Both\nexist so neither a REST reader nor an agent has to translate.\n"
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "empresa": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "rut": {
                "$ref": "#/components/schemas/Rut"
              }
            }
          },
          "tipo_dte": {
            "$ref": "#/components/schemas/TipoDte"
          },
          "issue_date": {
            "type": "string",
            "format": "date"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "resolved_receptor": {
            "type": "object",
            "description": "How the receptor was identified, and to what.",
            "properties": {
              "rut": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "legal_name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "resolution_method": {
                "type": "string",
                "enum": [
                  "rut_exact",
                  "anonymous",
                  "needs_clarification"
                ],
                "description": "`needs_clarification` means a name was given rather than a RUT, so the document\nwould go to the anonymous consumer. Ask for the RUT; we will not guess one.\n"
              }
            }
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DocumentItem"
            }
          },
          "totals": {
            "$ref": "#/components/schemas/Totals"
          },
          "references": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DocumentReference"
            }
          },
          "prices_include_tax": {
            "type": "boolean"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "folio_preview": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The folio that WOULD be used, read from the counter and not reserved.\n\nIN THE SANDBOX IT IS ALWAYS A NUMBER (§4.2, #520): sandbox folios are virtual —\n`1..1000000`, no CAF, no timbraje — and the counter row is created at the first\nemission, so a brand-new empresa forecasts `1` rather than `null`. It used to answer\n`null` from the absent row while `GET /v1/folios` answered «next folio 1, 1000000\nleft, ok» from the same absence, and the preview then warned that emitting would fail;\nit did not.\n\n`null` means «not forecastable», and there are exactly two ways to reach it: a LIVE\nmercado empresa with no folios left (or past the end of its CAF), and any SFG empresa,\nwhere the SII assigns the folio at emission and there is nothing of ours to forecast.\n"
          },
          "folio": {
            "type": [
              "integer",
              "null"
            ],
            "description": "ALWAYS `null`. No folio was taken, and saying so explicitly beats omitting it — the\nforecast lives in `folio_forecast` beside it.\n\nDeclared `[integer, \"null\"]` and not the bare `\"null\"` it used to be: a pure-null\nproperty aborts `openapi-generator`'s Python target for the WHOLE document\n(`RuntimeException: Codegen Property not yet supported in getPydanticType`, #476), so\none field nobody reads cost every operation in the contract. The `integer` half names\nwhat the value would be if it were ever not null; the sentence above is the promise.\n"
          },
          "emitted": {
            "type": "boolean",
            "description": "True once this preview has produced a document."
          },
          "consumed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Present on a read; when the preview was emitted."
          },
          "warnings": {
            "type": "array",
            "description": "Non-blocking findings. Empty on a preview with nothing to say, which is the ordinary\ncase — and, in the SANDBOX, the only case a stock warning can produce (#520).\n\n**Branch on `code`, never on `message`** — the same rule the error envelope states.\nThe message is English prose written for a developer reading a log; a panel showing\nthis to a person maps the `code` to its own Spanish, which it cannot do by matching a\nsentence that may be reworded.\n",
            "items": {
              "$ref": "#/components/schemas/PreviewWarning"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "One hour after creation. An hour is a conversation, not a record."
          }
        }
      },
      "PreviewWarning": {
        "type": "object",
        "title": "PreviewWarning",
        "description": "One non-blocking finding on a draft. Declared as a schema of its own (#520) because it was\nan inline `{code, message, param}` with no vocabulary published anywhere — so a panel that\nwanted to show a person Spanish had to match the English sentence, and an agent had nothing\nstable to branch on.\n\nA warning NEVER blocks the emission. `POST /documents` with this `preview_id` proceeds; if\na finding were a reason to refuse, it would be a `422` with an error code instead.\n",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "receptor_unresolved",
              "no_folios_available"
            ],
            "description": "The stable identifier. §5.8's twelve-month promise applies here as it does to an error\n`code`, and the enum grows additively — an unrecognised value is never a fault, so\ntreat one you do not know as «something worth relaying, verbatim».\n\n  * `receptor_unresolved` — a NAME was given where a RUT belongs, so the document would\n    be addressed to the anonymous consumer rather than to the person meant. `param` is\n    `receptor.rut`. Ask for the RUT; we will not guess one.\n  * `no_folios_available` — LIVE `emission_channel=mercado` only. The empresa has no\n    folio left for this tipo (or is past the end of its CAF), and emitting will fail\n    until folios are requested. It cannot arrive from the SANDBOX, where folios are\n    virtual and there is nothing to request, nor from an SFG empresa, where the SII\n    assigns the folio at emission.\n"
          },
          "message": {
            "type": "string",
            "description": "ENGLISH. Developer prose, like `error.message` — read `info.description`'s language\nrule. Show it if you must; branch on `code`.\n"
          },
          "param": {
            "type": "string",
            "description": "The request field the finding is about, when it is about one.",
            "examples": [
              "receptor.rut"
            ]
          }
        }
      },
      "PublicLinks": {
        "type": "object",
        "description": "Freshly minted public links. Returned by `POST /documents/{document_id}/public_links` and\n`POST /tax/f29/proposals/{period}/public_links`.\n\nTHE URLS EXIST ONLY HERE. The secret half of each link is stored hashed, exactly as an API\nkey is, so a working URL cannot be reconstructed afterwards — the property that stops a\ndatabase dump from being a document dump. Capture what you mint.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "public_links"
          },
          "document_id": {
            "type": "string",
            "description": "Present when the subject is a document."
          },
          "period": {
            "$ref": "#/components/schemas/Period"
          },
          "proposal_id": {
            "type": "string",
            "description": "Present when the subject is an F29 propuesta."
          },
          "empresa": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "rut": {
                "$ref": "#/components/schemas/Rut"
              }
            }
          },
          "pdf_public_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "xml_public_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "cedible_public_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Present only when `cedible` was in the minting request's `artifacts` (ticket #18 wave\n10). `null` otherwise — including on a document whose tipo cannot carry one.\n"
          },
          "upload_file_public_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "expires_in_seconds": {
            "type": [
              "integer",
              "null"
            ]
          },
          "expires_in_days": {
            "type": [
              "number",
              "null"
            ],
            "description": "The same lifetime in days, deliberately fractional: a ten-minute link is `0.00694`, and\nreporting it as `0` would be a different and wrong statement.\n"
          }
        }
      },
      "ErrorCode": {
        "type": "object",
        "description": "One entry of the published error dictionary (`GET /error_codes`).",
        "properties": {
          "code": {
            "type": "string",
            "examples": [
              "ted_signature_invalid"
            ]
          },
          "type": {
            "type": "string",
            "description": "The `error.type` family it arrives under, or `rejection` for a code that appears inside\na document's `rejection_reasons[]` rather than as a response.\n",
            "examples": [
              "rejection"
            ]
          },
          "http_status": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Null for `rejection` rows, which are not responses."
          },
          "sii_code": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "TED-2-510"
            ]
          },
          "message": {
            "type": "string"
          },
          "hint": {
            "type": "string"
          },
          "retriable": {
            "type": "boolean",
            "description": "Could the IDENTICAL request succeed later?"
          },
          "doc_url": {
            "type": "string",
            "format": "uri",
            "description": "The same URL `error.doc_url` carries."
          }
        }
      },
      "Document": {
        "type": "object",
        "description": "An emitted DTE. It carries NO public-link field: `pdf_public_url` and `xml_public_url` were\nwithdrawn (ticket #18) because the link secret is stored hashed and unrecoverable, so a read\ncould only ever answer `null` or a URL that 404s. A working, `?k=`-bearing URL exists solely\ninside the `public_links` object of the response that MINTED it — `POST\n/documents/{document_id}/public_links`, or an emission that asked for one (see\n`EmittedDocument`). Reads, lists, expansions and webhook payloads never carry one.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "document"
          },
          "id": {
            "type": "string"
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "empresa": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "rut": {
                "$ref": "#/components/schemas/Rut"
              }
            }
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "tipo_dte": {
            "$ref": "#/components/schemas/TipoDte"
          },
          "folio": {
            "type": [
              "integer",
              "null"
            ]
          },
          "issue_date": {
            "type": "string",
            "format": "date"
          },
          "status": {
            "$ref": "#/components/schemas/DocumentStatus"
          },
          "receptor": {
            "$ref": "#/components/schemas/Party"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DocumentItem"
            }
          },
          "totals": {
            "$ref": "#/components/schemas/Totals"
          },
          "references": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DocumentReference"
            }
          },
          "pdf_url": {
            "type": "string",
            "format": "uri"
          },
          "xml_url": {
            "type": "string",
            "format": "uri"
          },
          "xml": {
            "type": "string",
            "description": "Present only with `expand=xml`."
          },
          "pdf_base64": {
            "type": "string",
            "description": "Present only with `expand=pdf_base64`."
          },
          "sii": {
            "$ref": "#/components/schemas/SiiState"
          },
          "cesion": {
            "$ref": "#/components/schemas/CesionState"
          },
          "rejection_reasons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RejectionReason"
            }
          },
          "annulled_by": {
            "type": [
              "string",
              "null"
            ],
            "description": "Document id of the nota de crédito that annulled this one."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "accepted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "EmittedDocument": {
        "description": "The document object as returned by `POST /documents` — the one response that can MINT a\npublic link, and therefore the one response that can carry a working URL. It is a\n`Document` plus an optional `public_links` object; nothing else about the shape differs.\n",
        "allOf": [
          {
            "$ref": "#/components/schemas/Document"
          },
          {
            "type": "object",
            "properties": {
              "public_links": {
                "type": "object",
                "description": "Present ONLY when the emission asked for `pdf: {public: true}` and/or\n`xml: {public: true}` — ABSENT, never null, otherwise. These URLs include their\n`?k=` secret and are shown exactly once: the secret is stored hashed, so no later\nrequest can reproduce them. Capture them here or mint fresh ones with\n`POST /documents/{document_id}/public_links`.\n\nNo expiry is echoed: `pdf` and `xml` carry independent `link_expires_in_*` values\nat emission, so a single pair of expiry fields would be wrong whenever they differ.\n",
                "properties": {
                  "object": {
                    "type": "string",
                    "const": "public_links"
                  },
                  "pdf_public_url": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri",
                    "description": "Null when only the XML link was requested."
                  },
                  "xml_public_url": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri",
                    "description": "Null when only the PDF link was requested."
                  }
                }
              }
            }
          }
        ]
      },
      "SiiState": {
        "type": "object",
        "properties": {
          "environment": {
            "type": "string",
            "enum": [
              "sandbox",
              "certificacion",
              "produccion"
            ]
          },
          "track_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "estado": {
            "type": [
              "string",
              "null"
            ],
            "description": "Raw SII estado — DOK, DNK, FAU, FNA, FAN, EMP, EPR, RCT, RPR, RLV…"
          },
          "glosa": {
            "type": [
              "string",
              "null"
            ]
          },
          "err_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "num_atencion": {
            "type": [
              "string",
              "null"
            ],
            "description": "SII case number. Quote it to the SII mesa de ayuda."
          },
          "modified_by": {
            "type": "array",
            "description": "Notas de crédito/débito the SII reports against this document (TMD/TMC/MMD/MMC).",
            "items": {
              "type": "string"
            }
          },
          "received_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_checked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "CesionState": {
        "type": "object",
        "description": "Ley 19.983 cesibilidad, as the SII's registro reports it (ticket #47). A CACHE of one\nremote answer, never a local computation: whether a factura can be assigned to a\nfactoring company depends on the receptor's acuse — or eight días corridos of their\nsilence — and on whether the emisor declared the sale al contado, and only the SII knows.\n\nAll three fields are null until `POST /documents/{document_id}/cesion` has been answered\nonce. NOT the same thing as the cedible PDF copy, which exists by tipo and is served by\n`GET /documents/{document_id}/pdf?copy=cedible`.\n",
        "properties": {
          "cedible": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "True for SII response codes 23 (express acuse) and 25 (the eight days lapsed), false\nfor 24 and for 21 (an annulment credit note references the factura). Null when nobody\nhas asked yet, or when the SII answered a code we do not read as a verdict —\n`sii_status_code` carries it verbatim rather than us guessing.\n"
          },
          "sii_status_code": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The raw `codResp` from `consultarDocDteCedible`.",
            "examples": [
              25
            ]
          },
          "checked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the SII last ANSWERED — not when we last tried. A transient wire failure leaves\nthis untouched so the queue's own retry re-asks.\n"
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "queued",
              "submitted",
              "aec_generated",
              "confirmed",
              "refused",
              "failed",
              "simulated",
              null
            ],
            "description": "The newest `POST /documents/{document_id}/cede` attempt's outcome (ticket #60) — a\nDIFFERENT question from `cedible` above it (\"could this document be cedida at all?\"\nvs. \"did an actual cede attempt go through?\"). Null when no attempt has ever been\nmade. Present only from `GET /documents/{document_id}` — omitted from list reads and\nfrom other actions' responses, which do not pay for the extra lookup.\n\n`queued` (accepted, not yet picked up) → one of:\n\n* `submitted` — the AEC was assembled, signed and UPLOADED to the SII's Registro\n  Electrónico de Transferencia de Crédito, which answered «Envío recibido OK» and\n  issued a TrackID. **THIS IS NOT REGISTRATION** (ticket #69). The registry validates\n  afterwards and notifies a rejection by e-mail to the cedente, hours later. Do not\n  settle money on this value.\n* `confirmed` — the registry's own `wsRPETCConsulta.getEstEnvio` answered `EOK`\n  («Envío Aceptado») for that TrackID. That is the SII saying the cesión is anotada,\n  and it is the only thing that writes this status. An attempt whose envelope never\n  settles within the poll budget KEEPS `submitted` — the AEC really was received —\n  while its job settles `failed` with `cesion_registration_unconfirmed`.\n* `refused` — the SII said no: it refused the upload (the authenticated user has no\n  «Envío de Documentos» permission for the cedente empresa, or the empresa is not a\n  DTE emisor), or the registry rejected the envelope at one of its validation stages.\n  Terminal, and never a transport problem.\n* `failed` — a fenced attempt, a malformed upload, an SII-side internal error, a\n  transport failure, or an answer nobody recognises and archived for review rather\n  than acted on.\n* `aec_generated` — LEGACY. The retired Portal MIPYME walk reached its «AEC ha sido\n  generado exitosamente» page, which produced a FILE and registered nothing. Nothing\n  writes this any more; it remains only so historical attempts keep their meaning.\n* `simulated` — test mode; nothing reached the SII.\n"
          }
        }
      },
      "CesionJob": {
        "type": "object",
        "description": "The `job` object `POST /documents/{document_id}/cede` returns (ticket",
        "properties": {
          "object": {
            "type": "string",
            "const": "job"
          },
          "id": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": "string",
            "const": "cesion_submit"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "succeeded",
              "failed"
            ]
          },
          "empresa_id": {
            "type": "string"
          },
          "document_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "cesionario_rut": {
            "$ref": "#/components/schemas/Rut"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RejectionReason": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "examples": [
              "ted_signature_invalid"
            ]
          },
          "sii_code": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "TED-2-510"
            ]
          },
          "message": {
            "type": "string"
          },
          "hint": {
            "type": "string"
          },
          "retriable": {
            "type": "boolean"
          }
        }
      },
      "DocumentEvent": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "examples": [
              "document.accepted"
            ]
          },
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "detail": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "CommercialResponseState": {
        "type": "string",
        "enum": [
          "pending",
          "accepted",
          "rejected",
          "receipt_acknowledged",
          "expired_accepted",
          "not_applicable"
        ]
      },
      "CommercialResponse": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "commercial_response"
          },
          "received_document_id": {
            "type": "string"
          },
          "action": {
            "type": "string",
            "enum": [
              "accept",
              "reject",
              "acknowledge_receipt"
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "content",
              "partial_missing",
              "total_missing",
              null
            ]
          },
          "sii_code": {
            "type": "string",
            "enum": [
              "ACD",
              "RCD",
              "ERM",
              "RFP",
              "RFT"
            ]
          },
          "registered_at": {
            "type": "string",
            "format": "date-time"
          },
          "sii": {
            "type": "object",
            "properties": {
              "estado": {
                "type": "string"
              },
              "glosa": {
                "type": "string"
              }
            }
          },
          "state": {
            "$ref": "#/components/schemas/CommercialResponseState"
          }
        }
      },
      "ReceivedDocument": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "received_document"
          },
          "id": {
            "type": "string"
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "tipo_dte": {
            "$ref": "#/components/schemas/TipoDte"
          },
          "folio": {
            "type": "integer"
          },
          "emisor": {
            "$ref": "#/components/schemas/Party"
          },
          "issue_date": {
            "type": "string",
            "format": "date"
          },
          "received_at": {
            "type": "string",
            "format": "date-time"
          },
          "source": {
            "type": "string",
            "enum": [
              "intercambio",
              "rcv",
              "manual"
            ]
          },
          "totals": {
            "$ref": "#/components/schemas/Totals"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DocumentItem"
            }
          },
          "references": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DocumentReference"
            }
          },
          "xml_url": {
            "type": "string",
            "format": "uri"
          },
          "pdf_url": {
            "type": "string",
            "format": "uri"
          },
          "raw_xml": {
            "type": "string",
            "description": "Present only with `expand=raw_xml`. The supplier's original bytes, signature intact."
          },
          "sii": {
            "type": "object",
            "properties": {
              "estado": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "cedible": {
                "type": "boolean"
              }
            }
          },
          "rcv": {
            "type": "object",
            "properties": {
              "entry_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "estado_contable": {
                "$ref": "#/components/schemas/EstadoContable"
              },
              "period": {
                "$ref": "#/components/schemas/Period"
              }
            }
          },
          "commercial_response": {
            "type": "object",
            "properties": {
              "state": {
                "$ref": "#/components/schemas/CommercialResponseState"
              },
              "state_reason": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "anchor_never_registered",
                  null
                ],
                "description": "Why the state is what it is, when the state name understates it. `null` for\nevery ordinary state. `anchor_never_registered` on an `expired_accepted`\nwhose SII receipt timestamp never arrived, so the window closed without a\ndeadline ever being asserted — `deadline_at` and `days_remaining` are `null`\non such a document and this is what says why.\n"
              },
              "deadline_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "End of the statutory 8-day window: the last second of the Santiago civil day\n`date(sii_receipt_at, America/Santiago) + 8`, with Chile's offset (`-04:00`\nor `-03:00`). Types 33/34/43 only.\n"
              },
              "days_remaining": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Whole Santiago civil days left in the statutory 8-day window. `0` means\n\"today is the last day\" — the window is STILL OPEN, not expired; cross-check\n`state`/`state_reason` (or `windowState` server-side) rather than treating `0`\nas a terminal value. Once the deadline has passed this is NEGATIVE — the\ncount of days overdue — rather than saturating at `0`, so a document whose\nwindow closed yesterday and one that closed three weeks ago are\ndistinguishable from this field alone. `null` only when there is no deadline\nto count toward (no SII receipt timestamp yet, or not a 33/34/43).\n"
              },
              "available_actions": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "accept",
                    "reject_content",
                    "reject_partial",
                    "reject_total",
                    "acknowledge_receipt"
                  ]
                }
              },
              "default_if_silent": {
                "type": "string",
                "const": "accepted",
                "description": "Aceptación tácita — doing nothing accepts on day 8. This is why nothing is automatic."
              },
              "history": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CommercialResponse"
                }
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EstadoContable": {
        "type": "string",
        "enum": [
          "REGISTRO",
          "PENDIENTE",
          "NO_INCLUIR",
          "RECLAMADO"
        ]
      },
      "Characterization": {
        "type": "object",
        "properties": {
          "tipo_compra": {
            "type": "string",
            "enum": [
              "del_giro",
              "supermercado",
              "activo_fijo",
              "iva_uso_comun",
              "no_recuperable"
            ]
          },
          "iva_uso_comun": {
            "type": "boolean"
          },
          "iva_no_recuperable_code": {
            "type": [
              "integer",
              "null"
            ],
            "description": "SII reason code, required when `tipo_compra` is `no_recuperable`."
          }
        }
      },
      "CharacterizationAction": {
        "type": "object",
        "description": "A queued caracterización write. `status` settles to `applied` only when the write really\nreached the SII; a run made with live writes disabled settles to `dry_run` and the SII's\nregister is unchanged.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "characterization_action"
          },
          "id": {
            "type": "string"
          },
          "rcv_entry_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "queued",
              "submitting",
              "applied",
              "dry_run",
              "rejected",
              "failed",
              "refused"
            ]
          },
          "tipo_compra": {
            "type": "string",
            "enum": [
              "del_giro",
              "supermercado",
              "activo_fijo",
              "iva_uso_comun",
              "no_recuperable"
            ]
          },
          "iva_no_recuperable_code": {
            "type": [
              "integer",
              "null"
            ]
          },
          "previous_tipo_compra": {
            "type": [
              "string",
              "null"
            ]
          },
          "reverts": {
            "type": "string",
            "description": "Present only on a revert — the id of the action being undone."
          },
          "dry_run": {
            "type": "boolean",
            "description": "TRUE until an executor that really wrote to the SII clears it."
          },
          "live_writes_enabled": {
            "type": "boolean",
            "description": "Whether a caracterización queued by this build can reach the SII at all. FALSE means the\naction is a rehearsal by construction: it will settle as `status: \"dry_run\"` and the SII's\ntipo de compra will not change. Distinct from `dry_run`, which is TRUE on any row that has\nnot been submitted YET.\n"
          },
          "note": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RcvEntry": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "rcv_entry"
          },
          "id": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "purchase",
              "sale"
            ]
          },
          "period": {
            "$ref": "#/components/schemas/Period"
          },
          "tipo_dte": {
            "type": "integer"
          },
          "folio": {
            "type": "integer"
          },
          "counterparty": {
            "$ref": "#/components/schemas/Party"
          },
          "issue_date": {
            "type": "string",
            "format": "date"
          },
          "receipt_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "amounts": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Totals"
              },
              {
                "type": "object",
                "properties": {
                  "iva_no_recuperable": {
                    "$ref": "#/components/schemas/Clp"
                  }
                }
              }
            ]
          },
          "estado_contable": {
            "$ref": "#/components/schemas/EstadoContable",
            "description": "The SII's own value, from its register. Never written by\n`POST /tax/purchases/{entry_id}/estado_contable` — that records the caller's intent\nin `estado_contable_override` instead, so a resync can always correct us.\n"
          },
          "estado_contable_override": {
            "type": [
              "object",
              "null"
            ],
            "description": "The local bookkeeping estado a caller set, or null. Read by the F29 only when the empresa's `honor_estado_contable_override` is on.",
            "properties": {
              "value": {
                "type": "string",
                "enum": [
                  "REGISTRO",
                  "PENDIENTE",
                  "NO_INCLUIR"
                ]
              },
              "set_by": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The API key that set it."
              },
              "set_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "honored": {
                "type": "boolean",
                "description": "Whether the F29 will act on it."
              }
            }
          },
          "estado_contable_effective": {
            "$ref": "#/components/schemas/EstadoContable",
            "description": "What the F29 propuesta will actually use — the override when honored, the SII's value otherwise."
          },
          "missing_at_sii": {
            "type": "boolean",
            "description": "True when the SII's REGISTRO detalle for this entry's registry period stopped returning\nit — reclamado, anulado, or moved out of the registry. Excluded from every F29 aggregate\nwhile true. The entry is kept rather than deleted: it is evidence that it was there.\n"
          },
          "missing_at_sii_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "characterization": {
            "$ref": "#/components/schemas/Characterization"
          },
          "document_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set when this entry corresponds to a document Facturia emitted."
          },
          "received_document_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "synced_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RcvEntryList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/List"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/RcvEntry"
                }
              },
              "sync": {
                "type": "object",
                "description": "Freshness of the underlying SII sync. `degraded` means we know the data is stale.\n\n**Scoped to `period` when the request names one.** `last_synced_at` is then the\ncompletion of the newest sync THAT READ THAT MONTH, and `null` means the month has\nnever been fetched — the empty `data` above is \"unknown\", not \"no documents\". Without\na `period` filter the block describes the empresa's newest sync of any month.\n",
                "properties": {
                  "last_synced_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "fresh",
                      "syncing",
                      "stale",
                      "degraded"
                    ]
                  },
                  "period_closed": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        ]
      },
      "TaxSummary": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "tax_summary"
          },
          "period": {
            "$ref": "#/components/schemas/Period"
          },
          "purchases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaxSummaryLine"
            }
          },
          "sales": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaxSummaryLine"
            }
          },
          "totals": {
            "type": "object",
            "properties": {
              "iva_debito": {
                "$ref": "#/components/schemas/Clp"
              },
              "iva_credito": {
                "$ref": "#/components/schemas/Clp"
              }
            }
          }
        }
      },
      "TaxSummaryLine": {
        "type": "object",
        "properties": {
          "tipo_dte": {
            "type": "integer"
          },
          "count": {
            "type": "integer"
          },
          "net": {
            "$ref": "#/components/schemas/Clp"
          },
          "exempt": {
            "$ref": "#/components/schemas/Clp"
          },
          "iva": {
            "$ref": "#/components/schemas/Clp"
          },
          "total": {
            "$ref": "#/components/schemas/Clp"
          }
        }
      },
      "F29Proposal": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "f29_proposal"
          },
          "id": {
            "type": "string"
          },
          "period": {
            "$ref": "#/components/schemas/Period"
          },
          "status": {
            "type": "string",
            "enum": [
              "computing",
              "ready",
              "stale",
              "blocked"
            ]
          },
          "computed_at": {
            "type": "string",
            "format": "date-time"
          },
          "based_on": {
            "type": "object",
            "properties": {
              "rcv_synced_at": {
                "type": "string",
                "format": "date-time"
              },
              "purchases": {
                "type": "integer"
              },
              "sales": {
                "type": "integer"
              }
            }
          },
          "summary": {
            "type": "object",
            "properties": {
              "iva_debito": {
                "$ref": "#/components/schemas/Clp"
              },
              "iva_credito": {
                "$ref": "#/components/schemas/Clp"
              },
              "iva_retenido": {
                "$ref": "#/components/schemas/Clp"
              },
              "remanente_anterior": {
                "$ref": "#/components/schemas/Clp"
              },
              "ppm": {
                "$ref": "#/components/schemas/Clp"
              },
              "total_payable": {
                "$ref": "#/components/schemas/Clp"
              },
              "due_date": {
                "type": "string",
                "format": "date"
              }
            }
          },
          "codes": {
            "type": "array",
            "description": "Exactly what the upload file will carry, code for code.",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "integer",
                  "examples": [
                    538,
                    537,
                    39,
                    519,
                    520,
                    77,
                    62,
                    91
                  ]
                },
                "label": {
                  "type": "string"
                },
                "value": {
                  "type": "integer"
                }
              }
            }
          },
          "observations": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "severity": {
                  "type": "string",
                  "enum": [
                    "info",
                    "warning",
                    "error"
                  ]
                },
                "message": {
                  "type": "string"
                },
                "entry_ids": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "upload_file_url": {
            "type": "string",
            "format": "uri"
          },
          "filing_id": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "F29Filing": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "f29_filing"
          },
          "id": {
            "type": "string"
          },
          "period": {
            "$ref": "#/components/schemas/Period"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "preparing",
              "uploading",
              "submitted",
              "dry_run",
              "accepted",
              "rejected",
              "failed",
              "refused"
            ],
            "description": "Modelled as a job because that is what it is: the SII has no F29 webservice, so\nfiling is a certificate-authenticated session submitting a fixed-format .txt.\n\n`dry_run` is the terminal state of a rehearsal: the upload file was built and validated\nand NO request was made to the SII. It does not hold the period — a later filing for the\nsame month is accepted rather than answered `409 f29_filing_already_in_flight`.\n`submitted` means the SII has the file and has not answered yet, and that one does hold\nthe period.\n"
          },
          "declared": {
            "type": "object",
            "properties": {
              "total_payable": {
                "$ref": "#/components/schemas/Clp"
              }
            }
          },
          "attempts": {
            "type": "integer"
          },
          "folio": {
            "type": [
              "string",
              "null"
            ]
          },
          "receipt_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RejectionReason"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ExpenseCreateRequest": {
        "type": "object",
        "required": [
          "provider",
          "amount_original",
          "currency",
          "date"
        ],
        "properties": {
          "provider": {
            "type": "object",
            "required": [
              "name"
            ],
            "properties": {
              "name": {
                "type": "string"
              },
              "country": {
                "type": "string",
                "description": "ISO 3166-1 alpha-2."
              },
              "rut": {
                "$ref": "#/components/schemas/Rut"
              }
            }
          },
          "description": {
            "type": "string"
          },
          "amount_original": {
            "type": "number"
          },
          "currency": {
            "type": "string",
            "examples": [
              "USD",
              "EUR"
            ]
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "fx_rate": {
            "type": "number",
            "description": "REQUIRED whenever `currency` is not `CLP`; omitting it returns `400 fx_rate_required`.\nFacturia never invents a rate — it sets the peso base of a tax document.\n"
          },
          "fx_source": {
            "type": "string",
            "enum": [
              "card_rate",
              "sii_observado",
              "custom"
            ],
            "description": "`card_rate` (the settlement rate the issuer actually applied) is the documented\nrecommendation. CAVEAT: which rate is definitively correct for tipo 46 on foreign\ndigital services is under review with an accountant and is not settled by a citable\nruling; the value you send is stored verbatim and shown on the document.\n"
          },
          "external_id": {
            "type": "string"
          }
        }
      },
      "Expense": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ExpenseCreateRequest"
          },
          {
            "type": "object",
            "properties": {
              "object": {
                "type": "string",
                "const": "expense"
              },
              "id": {
                "type": "string"
              },
              "source": {
                "type": "string",
                "enum": [
                  "api",
                  "fintoc",
                  "statement"
                ],
                "default": "api",
                "description": "How this charge arrived. A connector-created expense is otherwise indistinguishable\nfrom one you posted yourself.\n"
              },
              "connection_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "statement_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "amount_clp": {
                "$ref": "#/components/schemas/Clp"
              },
              "period": {
                "$ref": "#/components/schemas/Period"
              },
              "suggestion_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "document_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        ]
      },
      "Connection": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "connection"
          },
          "id": {
            "type": "string"
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "provider": {
            "type": "string",
            "enum": [
              "fintoc"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending_link",
              "active",
              "requires_relink",
              "disconnected",
              "error"
            ],
            "description": "`requires_relink` is the near-universal failure mode of bank feeds; it raises\n`connection.requires_relink` so a wizard can prompt the user instead of the feed\nsilently going quiet.\n"
          },
          "link_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "accounts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "checking",
                    "credit_card"
                  ]
                },
                "name": {
                  "type": "string"
                },
                "currency": {
                  "type": "string"
                },
                "last4": {
                  "type": "string"
                }
              }
            }
          },
          "sync_frequency": {
            "type": "string",
            "enum": [
              "daily",
              "weekly",
              "manual"
            ]
          },
          "last_synced_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Statement": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "statement"
          },
          "id": {
            "type": "string"
          },
          "filename": {
            "type": "string"
          },
          "format": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "processing",
              "imported",
              "failed"
            ]
          },
          "rows_read": {
            "type": "integer"
          },
          "expenses_created": {
            "type": "integer"
          },
          "duplicates_skipped": {
            "type": "integer"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "row": {
                  "type": "integer"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          },
          "uploaded_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SuggestionType": {
        "type": "string",
        "enum": [
          "emit_46",
          "missing_characterization",
          "folios_low",
          "unclaimed_credit"
        ]
      },
      "Suggestion": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "suggestion"
          },
          "id": {
            "type": "string"
          },
          "type": {
            "$ref": "#/components/schemas/SuggestionType"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "confirmed",
              "auto_confirmed",
              "dismissed",
              "expired"
            ]
          },
          "period": {
            "$ref": "#/components/schemas/Period"
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "reason": {
            "type": "string"
          },
          "estimated_benefit": {
            "type": "object",
            "properties": {
              "iva_credit": {
                "$ref": "#/components/schemas/Clp"
              }
            }
          },
          "source": {
            "type": "object",
            "properties": {
              "object": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "origin": {
                "type": "string",
                "enum": [
                  "api",
                  "fintoc",
                  "statement"
                ],
                "description": "How the underlying charge reached Facturia."
              }
            }
          },
          "proposed_document": {
            "$ref": "#/components/schemas/DocumentCreateRequest"
          },
          "f29_effect": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "integer"
                },
                "value": {
                  "type": "integer"
                }
              }
            }
          },
          "document_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "webhook_endpoint"
          },
          "id": {
            "type": "string"
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "description": {
            "type": "string"
          },
          "enabled_events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "empresas": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Rut"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "enabled",
              "disabled"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookEndpointWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEndpoint"
          },
          {
            "type": "object",
            "properties": {
              "secret": {
                "type": "string",
                "description": "Returned once, on creation or rotation. Store it; it is never shown again.",
                "examples": [
                  "whsec_9Kd2mQ7xR4vN8pL1tY6bZ3wA5cE0uJhF"
                ]
              }
            }
          }
        ]
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "webhook_delivery"
          },
          "id": {
            "type": "string"
          },
          "webhook_endpoint_id": {
            "type": "string"
          },
          "event_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "succeeded",
              "failed"
            ]
          },
          "attempt": {
            "type": "integer"
          },
          "response_status": {
            "type": [
              "integer",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "next_attempt_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Retry schedule — 30s, 2m, 10m, 1h, 3h, 6h, 12h, 24h, with jitter."
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Event": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "event"
          },
          "id": {
            "type": "string",
            "examples": [
              "evt_01K2RG1P4M7X"
            ]
          },
          "type": {
            "type": "string",
            "examples": [
              "document.accepted"
            ]
          },
          "api_version": {
            "type": "string",
            "const": "v1"
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "empresa": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "rut": {
                "$ref": "#/components/schemas/Rut"
              }
            }
          },
          "data": {
            "type": "object",
            "properties": {
              "object": {
                "description": "The full current resource, identical to what GET would return.",
                "type": "object",
                "additionalProperties": true
              },
              "previous_attributes": {
                "type": "object",
                "description": "Present on updates; holds only what changed.",
                "additionalProperties": true
              },
              "detail": {
                "type": "object",
                "description": "The SUB-OBJECT this event is actually about, when `object` is a wider resource\n(#414). `empresa.onboarding.updated` always carries the full onboarding body as\n`object` — never a mandato, a go-live authorization, or a certification file, the\nfour shapes it used to arrive in — and the thing that changed rides here instead.\n`empresa.onboarding.step.completed` uses it to carry `{ \"step\": \"<step_key>\" }`.\n\nNOT the same idea as `previous_attributes`: that says what a field on `object`\nUSED TO BE; this is a different object entirely.\n",
                "additionalProperties": true
              }
            }
          }
        }
      },
      "UsageMetric": {
        "type": "string",
        "enum": [
          "document_accepted",
          "received_document",
          "f29_filing",
          "empresa_active"
        ]
      },
      "UsageSummary": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "usage_summary"
          },
          "period": {
            "$ref": "#/components/schemas/Period"
          },
          "as_of": {
            "type": "string",
            "format": "date-time"
          },
          "totals": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "by_empresa": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "empresa": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "rut": {
                      "$ref": "#/components/schemas/Rut"
                    }
                  }
                },
                "documents_accepted": {
                  "type": "integer"
                },
                "by_tipo_dte": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        }
      },
      "UsageRecord": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "usage_record"
          },
          "id": {
            "type": "string"
          },
          "metric": {
            "$ref": "#/components/schemas/UsageMetric"
          },
          "quantity": {
            "type": "integer"
          },
          "empresa": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "rut": {
                "$ref": "#/components/schemas/Rut"
              }
            }
          },
          "document_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time",
            "description": "For `document_accepted`, the moment the SII accepted it. That is the moment it enters the ledger."
          },
          "billing_period": {
            "$ref": "#/components/schemas/Period"
          },
          "reversal_of": {
            "type": [
              "string",
              "null"
            ],
            "description": "Corrections are compensating records with negative quantity, never updates."
          }
        }
      },
      "Job": {
        "type": "object",
        "description": "One shape for every asynchronous operation the API starts. They differ in what they do and\nnot at all in how you watch them, which is the point of giving them one object and one\nendpoint (`GET /jobs/{job_id}`).\n\n`type` decides two things a caller needs: which scope may read the job (the scope that\nSTARTS that kind of job, plus the read scope for the subject it acts on), and the shape of\n`result`.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "job"
          },
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "description": "The closed set of job types this API writes. Enumerated here rather than left open\nbecause a generated client validates against it; `test/agent-contract.test.ts` reads\nevery `type` literal written into `sii_v1_jobs` across `src/` and `worker/src/` and\nfails if one is missing from this list, so the enum cannot drift from the writers.\n",
            "enum": [
              "folio_request",
              "document_send",
              "cesion_submit",
              "rcv_sync",
              "statement_import",
              "connection_sync"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "succeeded",
              "failed"
            ]
          },
          "mode": {
            "$ref": "#/components/schemas/Mode",
            "description": "The world this job belongs to (ADR-0004: sandbox-vs-real, unambiguous on every\nresponse). A job created in the other mode does not EXIST for your credential — the\nanswer is `404`, not `403`, exactly as it is on every list.\n"
          },
          "empresa_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "tipo_dte": {
            "type": [
              "integer",
              "null"
            ]
          },
          "quantity": {
            "type": [
              "integer",
              "null"
            ]
          },
          "result": {
            "$ref": "#/components/schemas/JobResult"
          },
          "error": {
            "$ref": "#/components/schemas/ErrorBody"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "JobResult": {
        "description": "What the job produced. **The shape is a function of the job's `type`**, so branch on `type`\nand read `result` second — the same rule `error.details` follows for `code`.\n\n`{}` while the job is `queued` or `running`, and for a failure whose executor recorded\nnothing beyond `error`. §5.8 applies: a job type added later brings a shape your client does\nnot know, and that is never a fault.\n",
        "oneOf": [
          {
            "title": "pending",
            "description": "Nothing recorded yet — queued, running, or a failure carrying only an `error`.",
            "type": "object",
            "maxProperties": 0
          },
          {
            "title": "folio_request",
            "description": "The range the SII actually granted. `quantity` here is the size REGISTERED, not the\n`quantity` asked for: the driver clamps to the SII's own per-tipo ceiling when the ask\nis above it, and the registered range is the only authoritative answer.\n",
            "type": "object",
            "required": [
              "tipo_dte",
              "quantity",
              "from",
              "to"
            ],
            "properties": {
              "tipo_dte": {
                "$ref": "#/components/schemas/TipoDte"
              },
              "quantity": {
                "type": "integer"
              },
              "caf_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "`null` in the sandbox, where folios are virtual and no CAF is fetched."
              },
              "from": {
                "type": "integer"
              },
              "to": {
                "type": "integer"
              },
              "note": {
                "type": "string"
              }
            }
          },
          {
            "title": "folio_request (failed)",
            "description": "`range_registered` is the fact that decides whether a retry is safe: once a range IS\nregistered a second attempt would assign ANOTHER one and walk the empresa toward the\nSII's unused-range limit.\n",
            "type": "object",
            "required": [
              "range_registered"
            ],
            "properties": {
              "range_registered": {
                "type": "boolean"
              }
            }
          },
          {
            "title": "document_send",
            "type": "object",
            "required": [
              "delivered",
              "to"
            ],
            "properties": {
              "delivered": {
                "type": "boolean"
              },
              "simulated": {
                "type": "boolean",
                "description": "Test mode — nothing reached a real mailbox (§4.2)."
              },
              "provider": {
                "type": "string"
              },
              "provider_message_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "to": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "email"
                }
              },
              "attach": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "pdf",
                    "xml"
                  ]
                }
              }
            }
          },
          {
            "title": "cesion_submit",
            "description": "How far the irreversible act got. `status: submitted` with `registration: pending` is the\nfurthest the upload itself can reach — a receipt is not a registration (ticket #69) — and\nthe job stays `running` until the registry's own `getEstEnvio` settles it. `rtc_track_id`\nis the SII's receipt number for the envelope, and is what the SII's own «Consulta\nResultado de Envíos» page takes.\n",
            "type": "object",
            "required": [
              "step_reached"
            ],
            "properties": {
              "status": {
                "type": "string",
                "examples": [
                  "submitted"
                ]
              },
              "step_reached": {
                "type": "string"
              },
              "registration": {
                "type": "string",
                "enum": [
                  "pending",
                  "confirmed"
                ]
              },
              "rtc_track_id": {
                "type": [
                  "string",
                  "null"
                ],
                "examples": [
                  "0000038936"
                ]
              }
            }
          },
          {
            "title": "cesion_submit (fenced)",
            "description": "The live-cesión fence refused the attempt before anything was posted.",
            "type": "object",
            "required": [
              "fenced"
            ],
            "properties": {
              "fenced": {
                "type": "boolean"
              }
            }
          },
          {
            "title": "rcv_sync",
            "description": "What one period's read found. `purchases` and `sales` are the documents the SII's\nREGISTRO held for the period (created, updated or unchanged in the mirror); `created` and\n`updated` are how many of those moved this time. `withdrawn_at_sii` counts documents the\nregister stopped returning (excluded from the F29 until they come back). `partial_errors`\nnames any sub-registry the SII refused — the read still landed what it could.\nWhile the job is `queued` or `running` only `period` is present.\n",
            "type": "object",
            "required": [
              "period"
            ],
            "properties": {
              "period": {
                "$ref": "#/components/schemas/Period"
              },
              "purchases": {
                "type": "integer"
              },
              "sales": {
                "type": "integer"
              },
              "created": {
                "type": "integer"
              },
              "updated": {
                "type": "integer"
              },
              "changed": {
                "type": "boolean"
              },
              "withdrawn_at_sii": {
                "type": "integer"
              },
              "restored_at_sii": {
                "type": "integer"
              },
              "partial_errors": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "simulated": {
                "type": "boolean"
              }
            }
          },
          {
            "title": "statement_import",
            "type": "object",
            "required": [
              "statement_id"
            ],
            "properties": {
              "statement_id": {
                "type": "string"
              },
              "filename": {
                "type": "string"
              },
              "format": {
                "type": "string"
              }
            }
          },
          {
            "title": "connection_sync",
            "type": "object",
            "required": [
              "connection_id"
            ],
            "properties": {
              "connection_id": {
                "type": "string"
              },
              "imported": {
                "type": "integer"
              },
              "simulated": {
                "type": "boolean"
              }
            }
          }
        ]
      },
      "SendJob": {
        "title": "SendJob",
        "description": "The job `POST /documents/{document_id}/send` returns. It is a `Job` of type `document_send`\nplus the three fields that say WHAT was sent WHERE — they are on the response and were not\non `Job`, so a strict client dropped exactly the fields it needed to confirm the delivery\nit had just asked for.\n",
        "allOf": [
          {
            "$ref": "#/components/schemas/Job"
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "document_send"
                ]
              },
              "document_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "to": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "email"
                },
                "description": "The resolved recipient list — the `to` you sent, or the receptor's stored addresses."
              },
              "attach": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "pdf",
                    "xml"
                  ]
                }
              }
            }
          }
        ]
      },
      "StatementImportJob": {
        "title": "StatementImportJob",
        "description": "The job `POST /tax/statements` returns. A `Job` of type `statement_import` plus the two\nfields that name the cartola it is importing, both present on the response.\n",
        "allOf": [
          {
            "$ref": "#/components/schemas/Job"
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "statement_import"
                ]
              },
              "statement_id": {
                "type": "string"
              },
              "filename": {
                "type": "string"
              }
            }
          }
        ]
      },
      "RcvSeed": {
        "title": "RcvSeed",
        "description": "What `POST /test_helpers/rcv/seed` returns. NOT a job — the seed is written in the request,\nsynchronously, because the sandbox has no worker to wait for (§4.1).\n",
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "rcv_seed"
          },
          "empresa_id": {
            "type": "string"
          },
          "period": {
            "$ref": "#/components/schemas/Period"
          },
          "purchases": {
            "type": "integer",
            "description": "How many purchase rows were asked for."
          },
          "sales": {
            "type": "integer",
            "description": "How many sale rows were asked for."
          },
          "rows_written": {
            "type": "integer",
            "description": "Rows actually written. Re-seeding the same period is idempotent — the natural key is\nstable per index, so a second call updates rather than duplicating.\n"
          }
        }
      }
    }
  }
}
